From ec3b53cdc3adb03e3190e2cf09a6ebc5d00a2793 Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Thu, 4 Nov 2021 14:01:37 -0800 Subject: [PATCH] Refactor/user database models (#775) * fix build error * drop frontend.old * improve auto_init decorator * purge depreciated site settings * formatting * update init function * fix(backend): :bug: Fix password reset bug Co-authored-by: Hayden --- frontend.old/.env.development | 2 - frontend.old/README.md | 24 -- frontend.old/babel.config.js | 3 - .../Domain/MealPlan/MealPlanCard.vue | 173 ----------- .../Domain/MealPlan/MealPlanEditor.vue | 46 --- .../Domain/MealPlan/MealPlanNew.vue | 227 -------------- .../Domain/User/UserAvatar.vue | 61 ---- .../Page/Recipe/RecipeEditor.vue | 153 ---------- .../Page/Recipe/RecipeViewer.vue | 142 --------- frontend.old/frontend.Dockerfile | 22 -- frontend.old/jsconfig.json | 9 - frontend.old/package.json | 75 ----- .../img/icons/android-chrome-192x192.png | Bin 12734 -> 0 bytes .../icons/android-chrome-maskable-192x192.png | Bin 12734 -> 0 bytes .../icons/android-chrome-maskable-512x512.png | Bin 26836 -> 0 bytes .../img/icons/apple-touch-icon-120x120.png | Bin 7005 -> 0 bytes .../img/icons/apple-touch-icon-152x152.png | Bin 10255 -> 0 bytes .../img/icons/apple-touch-icon-180x180.png | Bin 13075 -> 0 bytes .../img/icons/apple-touch-icon-60x60.png | Bin 2881 -> 0 bytes .../img/icons/apple-touch-icon-76x76.png | Bin 3846 -> 0 bytes .../public/img/icons/apple-touch-icon.png | Bin 13075 -> 0 bytes .../public/img/icons/favicon-16x16.png | Bin 574 -> 0 bytes .../public/img/icons/favicon-32x32.png | Bin 1217 -> 0 bytes .../img/icons/msapplication-icon-144x144.png | Bin 8950 -> 0 bytes .../public/img/icons/mstile-150x150.png | Bin 10304 -> 0 bytes .../public/img/icons/safari-pinned-tab.svg | 3 - frontend.old/public/index.html | 22 -- frontend.old/src/App.vue | 138 --------- frontend.old/src/api/about.js | 80 ----- frontend.old/src/api/api-utils.js | 121 -------- frontend.old/src/api/apiRoutes.js | 90 ------ frontend.old/src/api/backup.js | 62 ---- frontend.old/src/api/category.js | 111 ------- frontend.old/src/api/groups.js | 53 ---- frontend.old/src/api/index.js | 37 --- frontend.old/src/api/mealplan.js | 57 ---- frontend.old/src/api/meta.js | 29 -- frontend.old/src/api/migration.js | 25 -- frontend.old/src/api/recipe.js | 181 ----------- frontend.old/src/api/settings.js | 19 -- frontend.old/src/api/shoppingLists.js | 33 -- frontend.old/src/api/signUps.js | 35 --- frontend.old/src/api/siteSettings.js | 71 ----- frontend.old/src/api/themes.js | 42 --- frontend.old/src/api/upload.js | 14 - frontend.old/src/api/users.js | 107 ------- .../src/components/Fallbacks/NoRecipe.vue | 17 -- .../src/components/Fallbacks/The404.vue | 51 ---- .../FormHelpers/CategoryTagSelector.vue | 134 --------- .../FormHelpers/ColorPickerDialog.vue | 65 ---- .../src/components/FormHelpers/DatePicker.vue | 27 -- .../components/FormHelpers/ImportOptions.vue | 69 ----- .../FormHelpers/LanguageSelector.vue | 48 --- .../FormHelpers/TimePickerDialog.vue | 42 --- .../ImportSummaryDialog/DataTable.vue | 46 --- .../components/ImportSummaryDialog/index.vue | 142 --------- .../src/components/Login/LoginDialog.vue | 28 -- .../src/components/Login/LoginForm.vue | 94 ------ .../src/components/Login/SignUpForm.vue | 141 --------- .../MealPlan/ShoppingListDialog.vue | 100 ------ .../src/components/UI/Buttons/TheButton.vue | 163 ---------- .../components/UI/Buttons/TheCopyButton.vue | 71 ----- .../components/UI/Buttons/TheDownloadBtn.vue | 54 ---- .../components/UI/Buttons/TheUploadBtn.vue | 89 ------ .../components/UI/Dialogs/BackupDialog.vue | 146 --------- .../components/UI/Dialogs/ImportDialog.vue | 119 -------- .../components/UI/Dialogs/SearchDialog.vue | 172 ----------- .../src/components/UI/GlobalSnackbar.vue | 46 --- frontend.old/src/components/UI/LogCard.vue | 81 ----- .../components/UI/Search/FuseSearchBar.vue | 80 ----- .../src/components/UI/Search/SearchBar.vue | 65 ---- .../src/components/UI/Search/SearchDialog.vue | 103 ------- frontend.old/src/components/UI/StatCard.vue | 103 ------- frontend.old/src/components/UI/TheAppBar.vue | 102 ------- .../src/components/UI/TheRecipeFab.vue | 247 --------------- frontend.old/src/components/UI/TheSidebar.vue | 253 ---------------- .../src/components/UI/TheSiteMenu.vue | 109 ------- frontend.old/src/i18n.js | 38 --- frontend.old/src/installCompAPI.js | 3 - frontend.old/src/main.js | 44 --- frontend.old/src/mixins/initials.js | 18 -- frontend.old/src/mixins/user.js | 24 -- frontend.old/src/mixins/utilMixins.js | 7 - frontend.old/src/mixins/validators.js | 15 - frontend.old/src/pages/404Page.vue | 13 - frontend.old/src/pages/Admin/About/index.vue | 130 -------- .../pages/Admin/Dashboard/BackupViewer.vue | 162 ---------- .../src/pages/Admin/Dashboard/EventViewer.vue | 135 --------- .../src/pages/Admin/Dashboard/index.vue | 126 -------- .../src/pages/Admin/ManageUsers/GroupCard.vue | 122 -------- .../Admin/ManageUsers/GroupDashboard.vue | 100 ------ .../Admin/ManageUsers/TheSignUpTable.vue | 230 -------------- .../pages/Admin/ManageUsers/TheUserTable.vue | 284 ------------------ .../src/pages/Admin/ManageUsers/index.vue | 68 ----- .../pages/Admin/Migration/MigrationCard.vue | 94 ------ .../pages/Admin/Migration/MigrationDialog.vue | 106 ------- .../src/pages/Admin/Migration/index.vue | 82 ----- .../src/pages/Admin/Profile/APITokenCard.vue | 150 --------- .../pages/Admin/Profile/ProfileGroupCard.vue | 211 ------------- .../pages/Admin/Profile/ProfileThemeCard.vue | 225 -------------- .../src/pages/Admin/Profile/UserCard.vue | 198 ------------ .../src/pages/Admin/Profile/index.vue | 35 --- .../pages/Admin/Settings/CreatePageDialog.vue | 98 ------ .../Admin/Settings/CustomPageCreator.vue | 126 -------- .../pages/Admin/Settings/HomePageSettings.vue | 220 -------------- .../src/pages/Admin/Settings/index.vue | 34 --- .../ToolBox/CategoryTagEditor/BulkAssign.vue | 149 --------- .../CategoryTagEditor/RemoveUnused.vue | 89 ------ .../Admin/ToolBox/CategoryTagEditor/index.vue | 237 --------------- .../pages/Admin/ToolBox/EventNotification.vue | 213 ------------- .../pages/Admin/ToolBox/RecipeOrganizer.vue | 91 ------ .../src/pages/Admin/ToolBox/index.vue | 62 ---- frontend.old/src/pages/Admin/index.vue | 13 - frontend.old/src/pages/Debug.vue | 19 -- frontend.old/src/pages/HomePage.vue | 75 ----- frontend.old/src/pages/LoginPage.vue | 40 --- frontend.old/src/pages/MealPlan/Planner.vue | 150 --------- frontend.old/src/pages/MealPlan/ThisWeek.vue | 75 ----- frontend.old/src/pages/Recipe/NewRecipe.vue | 126 -------- .../src/pages/Recipe/ScraperDebugger.vue | 61 ---- frontend.old/src/pages/Recipe/ViewRecipe.vue | 271 ----------------- frontend.old/src/pages/Recipes/AllRecipes.vue | 58 ---- .../src/pages/Recipes/CategoryTagPage.vue | 108 ------- frontend.old/src/pages/Recipes/CustomPage.vue | 75 ----- frontend.old/src/pages/Recipes/Favorites.vue | 49 --- .../src/pages/SearchPage/FilterSelector.vue | 52 ---- frontend.old/src/pages/SearchPage/index.vue | 168 ----------- frontend.old/src/pages/ShoppingList/index.vue | 281 ----------------- frontend.old/src/pages/SignUpPage.vue | 40 --- frontend.old/src/plugins/vuetify.js | 63 ---- frontend.old/src/registerServiceWorker.js | 39 --- frontend.old/src/routes/admin.js | 74 ----- frontend.old/src/routes/auth.js | 18 -- frontend.old/src/routes/general.js | 16 - frontend.old/src/routes/index.js | 51 ---- frontend.old/src/routes/meal.js | 51 ---- frontend.old/src/routes/recipes.js | 37 --- frontend.old/src/store/index.js | 80 ----- frontend.old/src/store/modules/groups.js | 39 --- frontend.old/src/store/modules/homePage.js | 44 --- frontend.old/src/store/modules/language.js | 54 ---- frontend.old/src/store/modules/recipes.js | 76 ----- .../src/store/modules/siteSettings.js | 47 --- frontend.old/src/store/modules/snackbar.js | 23 -- .../src/store/modules/userSettings.js | 121 -------- frontend.old/src/sw.js | 75 ----- frontend.old/vue.config.js | 50 --- .../Domain/Recipe/RecipeDialogBulkAdd.vue | 6 +- frontend/pages/user/profile/edit.vue | 24 +- makefile | 5 +- .../data_access_layer/access_model_factory.py | 6 - mealie/db/init_db.py | 7 - mealie/db/models/_all_models.py | 1 - mealie/db/models/_model_base.py | 3 - mealie/db/models/_model_utils.py | 119 -------- mealie/db/models/_model_utils/__init__.py | 1 + mealie/db/models/_model_utils/auto_init.py | 184 ++++++++++++ mealie/db/models/_model_utils/helpers.py | 39 +++ mealie/db/models/group/group.py | 5 +- mealie/db/models/recipe/assets.py | 7 +- mealie/db/models/recipe/category.py | 6 - mealie/db/models/recipe/recipe.py | 19 +- mealie/db/models/settings.py | 35 --- mealie/db/models/users/users.py | 63 ++-- mealie/routes/site_settings/site_settings.py | 11 - mealie/routes/users/passwords.py | 3 +- mealie/schema/admin/settings.py | 26 +- mealie/services/backups/imports.py | 24 +- mealie/services/migrations/_migration_base.py | 2 +- mealie/services/user_services/user_service.py | 6 +- poetry.lock | 219 ++++++++++---- .../integration_tests/test_settings_routes.py | 32 -- 172 files changed, 430 insertions(+), 12255 deletions(-) delete mode 100644 frontend.old/.env.development delete mode 100644 frontend.old/README.md delete mode 100644 frontend.old/babel.config.js delete mode 100644 frontend.old/component-stagin/Domain/MealPlan/MealPlanCard.vue delete mode 100644 frontend.old/component-stagin/Domain/MealPlan/MealPlanEditor.vue delete mode 100644 frontend.old/component-stagin/Domain/MealPlan/MealPlanNew.vue delete mode 100644 frontend.old/component-stagin/Domain/User/UserAvatar.vue delete mode 100644 frontend.old/component-stagin/Page/Recipe/RecipeEditor.vue delete mode 100644 frontend.old/component-stagin/Page/Recipe/RecipeViewer.vue delete mode 100644 frontend.old/frontend.Dockerfile delete mode 100644 frontend.old/jsconfig.json delete mode 100644 frontend.old/package.json delete mode 100644 frontend.old/public/img/icons/android-chrome-192x192.png delete mode 100644 frontend.old/public/img/icons/android-chrome-maskable-192x192.png delete mode 100644 frontend.old/public/img/icons/android-chrome-maskable-512x512.png delete mode 100644 frontend.old/public/img/icons/apple-touch-icon-120x120.png delete mode 100644 frontend.old/public/img/icons/apple-touch-icon-152x152.png delete mode 100644 frontend.old/public/img/icons/apple-touch-icon-180x180.png delete mode 100644 frontend.old/public/img/icons/apple-touch-icon-60x60.png delete mode 100644 frontend.old/public/img/icons/apple-touch-icon-76x76.png delete mode 100644 frontend.old/public/img/icons/apple-touch-icon.png delete mode 100644 frontend.old/public/img/icons/favicon-16x16.png delete mode 100644 frontend.old/public/img/icons/favicon-32x32.png delete mode 100644 frontend.old/public/img/icons/msapplication-icon-144x144.png delete mode 100644 frontend.old/public/img/icons/mstile-150x150.png delete mode 100644 frontend.old/public/img/icons/safari-pinned-tab.svg delete mode 100644 frontend.old/public/index.html delete mode 100644 frontend.old/src/App.vue delete mode 100644 frontend.old/src/api/about.js delete mode 100644 frontend.old/src/api/api-utils.js delete mode 100644 frontend.old/src/api/apiRoutes.js delete mode 100644 frontend.old/src/api/backup.js delete mode 100644 frontend.old/src/api/category.js delete mode 100644 frontend.old/src/api/groups.js delete mode 100644 frontend.old/src/api/index.js delete mode 100644 frontend.old/src/api/mealplan.js delete mode 100644 frontend.old/src/api/meta.js delete mode 100644 frontend.old/src/api/migration.js delete mode 100644 frontend.old/src/api/recipe.js delete mode 100644 frontend.old/src/api/settings.js delete mode 100644 frontend.old/src/api/shoppingLists.js delete mode 100644 frontend.old/src/api/signUps.js delete mode 100644 frontend.old/src/api/siteSettings.js delete mode 100644 frontend.old/src/api/themes.js delete mode 100644 frontend.old/src/api/upload.js delete mode 100644 frontend.old/src/api/users.js delete mode 100644 frontend.old/src/components/Fallbacks/NoRecipe.vue delete mode 100644 frontend.old/src/components/Fallbacks/The404.vue delete mode 100644 frontend.old/src/components/FormHelpers/CategoryTagSelector.vue delete mode 100644 frontend.old/src/components/FormHelpers/ColorPickerDialog.vue delete mode 100644 frontend.old/src/components/FormHelpers/DatePicker.vue delete mode 100644 frontend.old/src/components/FormHelpers/ImportOptions.vue delete mode 100644 frontend.old/src/components/FormHelpers/LanguageSelector.vue delete mode 100644 frontend.old/src/components/FormHelpers/TimePickerDialog.vue delete mode 100644 frontend.old/src/components/ImportSummaryDialog/DataTable.vue delete mode 100644 frontend.old/src/components/ImportSummaryDialog/index.vue delete mode 100644 frontend.old/src/components/Login/LoginDialog.vue delete mode 100644 frontend.old/src/components/Login/LoginForm.vue delete mode 100644 frontend.old/src/components/Login/SignUpForm.vue delete mode 100644 frontend.old/src/components/MealPlan/ShoppingListDialog.vue delete mode 100644 frontend.old/src/components/UI/Buttons/TheButton.vue delete mode 100644 frontend.old/src/components/UI/Buttons/TheCopyButton.vue delete mode 100644 frontend.old/src/components/UI/Buttons/TheDownloadBtn.vue delete mode 100644 frontend.old/src/components/UI/Buttons/TheUploadBtn.vue delete mode 100644 frontend.old/src/components/UI/Dialogs/BackupDialog.vue delete mode 100644 frontend.old/src/components/UI/Dialogs/ImportDialog.vue delete mode 100644 frontend.old/src/components/UI/Dialogs/SearchDialog.vue delete mode 100644 frontend.old/src/components/UI/GlobalSnackbar.vue delete mode 100644 frontend.old/src/components/UI/LogCard.vue delete mode 100644 frontend.old/src/components/UI/Search/FuseSearchBar.vue delete mode 100644 frontend.old/src/components/UI/Search/SearchBar.vue delete mode 100644 frontend.old/src/components/UI/Search/SearchDialog.vue delete mode 100644 frontend.old/src/components/UI/StatCard.vue delete mode 100644 frontend.old/src/components/UI/TheAppBar.vue delete mode 100644 frontend.old/src/components/UI/TheRecipeFab.vue delete mode 100644 frontend.old/src/components/UI/TheSidebar.vue delete mode 100644 frontend.old/src/components/UI/TheSiteMenu.vue delete mode 100644 frontend.old/src/i18n.js delete mode 100644 frontend.old/src/installCompAPI.js delete mode 100644 frontend.old/src/main.js delete mode 100644 frontend.old/src/mixins/initials.js delete mode 100644 frontend.old/src/mixins/user.js delete mode 100644 frontend.old/src/mixins/utilMixins.js delete mode 100644 frontend.old/src/mixins/validators.js delete mode 100644 frontend.old/src/pages/404Page.vue delete mode 100644 frontend.old/src/pages/Admin/About/index.vue delete mode 100644 frontend.old/src/pages/Admin/Dashboard/BackupViewer.vue delete mode 100644 frontend.old/src/pages/Admin/Dashboard/EventViewer.vue delete mode 100644 frontend.old/src/pages/Admin/Dashboard/index.vue delete mode 100644 frontend.old/src/pages/Admin/ManageUsers/GroupCard.vue delete mode 100644 frontend.old/src/pages/Admin/ManageUsers/GroupDashboard.vue delete mode 100644 frontend.old/src/pages/Admin/ManageUsers/TheSignUpTable.vue delete mode 100644 frontend.old/src/pages/Admin/ManageUsers/TheUserTable.vue delete mode 100644 frontend.old/src/pages/Admin/ManageUsers/index.vue delete mode 100644 frontend.old/src/pages/Admin/Migration/MigrationCard.vue delete mode 100644 frontend.old/src/pages/Admin/Migration/MigrationDialog.vue delete mode 100644 frontend.old/src/pages/Admin/Migration/index.vue delete mode 100644 frontend.old/src/pages/Admin/Profile/APITokenCard.vue delete mode 100644 frontend.old/src/pages/Admin/Profile/ProfileGroupCard.vue delete mode 100644 frontend.old/src/pages/Admin/Profile/ProfileThemeCard.vue delete mode 100644 frontend.old/src/pages/Admin/Profile/UserCard.vue delete mode 100644 frontend.old/src/pages/Admin/Profile/index.vue delete mode 100644 frontend.old/src/pages/Admin/Settings/CreatePageDialog.vue delete mode 100644 frontend.old/src/pages/Admin/Settings/CustomPageCreator.vue delete mode 100644 frontend.old/src/pages/Admin/Settings/HomePageSettings.vue delete mode 100644 frontend.old/src/pages/Admin/Settings/index.vue delete mode 100644 frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/BulkAssign.vue delete mode 100644 frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/RemoveUnused.vue delete mode 100644 frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue delete mode 100644 frontend.old/src/pages/Admin/ToolBox/EventNotification.vue delete mode 100644 frontend.old/src/pages/Admin/ToolBox/RecipeOrganizer.vue delete mode 100644 frontend.old/src/pages/Admin/ToolBox/index.vue delete mode 100644 frontend.old/src/pages/Admin/index.vue delete mode 100644 frontend.old/src/pages/Debug.vue delete mode 100644 frontend.old/src/pages/HomePage.vue delete mode 100644 frontend.old/src/pages/LoginPage.vue delete mode 100644 frontend.old/src/pages/MealPlan/Planner.vue delete mode 100644 frontend.old/src/pages/MealPlan/ThisWeek.vue delete mode 100644 frontend.old/src/pages/Recipe/NewRecipe.vue delete mode 100644 frontend.old/src/pages/Recipe/ScraperDebugger.vue delete mode 100644 frontend.old/src/pages/Recipe/ViewRecipe.vue delete mode 100644 frontend.old/src/pages/Recipes/AllRecipes.vue delete mode 100644 frontend.old/src/pages/Recipes/CategoryTagPage.vue delete mode 100644 frontend.old/src/pages/Recipes/CustomPage.vue delete mode 100644 frontend.old/src/pages/Recipes/Favorites.vue delete mode 100644 frontend.old/src/pages/SearchPage/FilterSelector.vue delete mode 100644 frontend.old/src/pages/SearchPage/index.vue delete mode 100644 frontend.old/src/pages/ShoppingList/index.vue delete mode 100644 frontend.old/src/pages/SignUpPage.vue delete mode 100644 frontend.old/src/plugins/vuetify.js delete mode 100644 frontend.old/src/registerServiceWorker.js delete mode 100644 frontend.old/src/routes/admin.js delete mode 100644 frontend.old/src/routes/auth.js delete mode 100644 frontend.old/src/routes/general.js delete mode 100644 frontend.old/src/routes/index.js delete mode 100644 frontend.old/src/routes/meal.js delete mode 100644 frontend.old/src/routes/recipes.js delete mode 100644 frontend.old/src/store/index.js delete mode 100644 frontend.old/src/store/modules/groups.js delete mode 100644 frontend.old/src/store/modules/homePage.js delete mode 100644 frontend.old/src/store/modules/language.js delete mode 100644 frontend.old/src/store/modules/recipes.js delete mode 100644 frontend.old/src/store/modules/siteSettings.js delete mode 100644 frontend.old/src/store/modules/snackbar.js delete mode 100644 frontend.old/src/store/modules/userSettings.js delete mode 100644 frontend.old/src/sw.js delete mode 100644 frontend.old/vue.config.js delete mode 100644 mealie/db/models/_model_utils.py create mode 100644 mealie/db/models/_model_utils/__init__.py create mode 100644 mealie/db/models/_model_utils/auto_init.py create mode 100644 mealie/db/models/_model_utils/helpers.py delete mode 100644 mealie/db/models/settings.py delete mode 100644 tests/integration_tests/test_settings_routes.py diff --git a/frontend.old/.env.development b/frontend.old/.env.development deleted file mode 100644 index 4afc44f70ac8..000000000000 --- a/frontend.old/.env.development +++ /dev/null @@ -1,2 +0,0 @@ -VUE_APP_API_BASE_URL=http://localhost:9000 -PREVIEW_BUNDLE=true \ No newline at end of file diff --git a/frontend.old/README.md b/frontend.old/README.md deleted file mode 100644 index 576b98091161..000000000000 --- a/frontend.old/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# frontend - -## Project setup -``` -npm install -``` - -### Compiles and hot-reloads for development -``` -npm run serve -``` - -### Compiles and minifies for production -``` -npm run build -``` - -### Lints and fixes files -``` -npm run lint -``` - -### Customize configuration -See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/frontend.old/babel.config.js b/frontend.old/babel.config.js deleted file mode 100644 index 162a3ea97c29..000000000000 --- a/frontend.old/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: ["@vue/cli-plugin-babel/preset"], -}; diff --git a/frontend.old/component-stagin/Domain/MealPlan/MealPlanCard.vue b/frontend.old/component-stagin/Domain/MealPlan/MealPlanCard.vue deleted file mode 100644 index 140e9872cb39..000000000000 --- a/frontend.old/component-stagin/Domain/MealPlan/MealPlanCard.vue +++ /dev/null @@ -1,173 +0,0 @@ - - - - - diff --git a/frontend.old/component-stagin/Domain/MealPlan/MealPlanEditor.vue b/frontend.old/component-stagin/Domain/MealPlan/MealPlanEditor.vue deleted file mode 100644 index 7f790b483491..000000000000 --- a/frontend.old/component-stagin/Domain/MealPlan/MealPlanEditor.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - diff --git a/frontend.old/component-stagin/Domain/MealPlan/MealPlanNew.vue b/frontend.old/component-stagin/Domain/MealPlan/MealPlanNew.vue deleted file mode 100644 index fd8fbe347ec2..000000000000 --- a/frontend.old/component-stagin/Domain/MealPlan/MealPlanNew.vue +++ /dev/null @@ -1,227 +0,0 @@ - - - diff --git a/frontend.old/component-stagin/Domain/User/UserAvatar.vue b/frontend.old/component-stagin/Domain/User/UserAvatar.vue deleted file mode 100644 index a2f119084b25..000000000000 --- a/frontend.old/component-stagin/Domain/User/UserAvatar.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/component-stagin/Page/Recipe/RecipeEditor.vue b/frontend.old/component-stagin/Page/Recipe/RecipeEditor.vue deleted file mode 100644 index 65ab9152a3ef..000000000000 --- a/frontend.old/component-stagin/Page/Recipe/RecipeEditor.vue +++ /dev/null @@ -1,153 +0,0 @@ - - - - - diff --git a/frontend.old/component-stagin/Page/Recipe/RecipeViewer.vue b/frontend.old/component-stagin/Page/Recipe/RecipeViewer.vue deleted file mode 100644 index c06b9a0cca14..000000000000 --- a/frontend.old/component-stagin/Page/Recipe/RecipeViewer.vue +++ /dev/null @@ -1,142 +0,0 @@ - - - - - diff --git a/frontend.old/frontend.Dockerfile b/frontend.old/frontend.Dockerfile deleted file mode 100644 index 2cdab5876dc9..000000000000 --- a/frontend.old/frontend.Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM node:lts-alpine - -# # install simple http server for serving static content -# RUN npm install -g http-server - -# make the 'app' folder the current working directory -WORKDIR /app - -# copy both 'package.json' and 'package-lock.json' (if available) -COPY package*.json ./ - -# install project dependencies -RUN npm install - -# copy project files and folders to the current working directory (i.e. 'app' folder) -# COPY . . - -# build app for production with minification -# RUN npm run build - -EXPOSE 8080 -CMD [ "npm", "run", "serve" ] \ No newline at end of file diff --git a/frontend.old/jsconfig.json b/frontend.old/jsconfig.json deleted file mode 100644 index 0486e3bb36a6..000000000000 --- a/frontend.old/jsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - } - }, - "exclude": ["node_modules", "dist"] -} diff --git a/frontend.old/package.json b/frontend.old/package.json deleted file mode 100644 index 1384c5140725..000000000000 --- a/frontend.old/package.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "frontend", - "version": "0.1.0", - "private": true, - "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", - "lint": "vue-cli-service lint", - "i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'" - }, - "dependencies": { - "@adapttive/vue-markdown": "^4.0.1", - "axios": "^0.21.1", - "core-js": "^3.14.0", - "fuse.js": "^6.4.6", - "register-service-worker": "^1.7.1", - "v-jsoneditor": "^1.4.4", - "vue": "^2.6.14", - "vue-i18n": "^8.24.1", - "vue-router": "^3.5.1", - "vuedraggable": "^2.24.3", - "vuetify": "^2.5.3", - "vuex": "^3.6.2", - "vuex-persistedstate": "^4.0.0-beta.3" - }, - "devDependencies": { - "@intlify/vue-i18n-loader": "^1.1.0", - "@mdi/font": "^5.9.55", - "@mdi/js": "^5.9.55", - "@vue/cli-plugin-babel": "^4.5.13", - "@vue/cli-plugin-eslint": "^4.5.13", - "@vue/cli-plugin-pwa": "~4.5.0", - "@vue/cli-service": "^4.5.13", - "@vue/preload-webpack-plugin": "^2.0.0", - "babel-eslint": "^10.1.0", - "eslint": "^6.7.2", - "eslint-plugin-vue": "^6.2.2", - "html-webpack-plugin": "^5.3.1", - "preload-webpack-plugin": "^2.3.0", - "sass": "^1.34.1", - "sass-loader": "^8.0.2", - "typeface-roboto": "^1.1.13", - "vue-cli-plugin-i18n": "~1.0.1", - "vue-cli-plugin-vuetify": "^2.4.1", - "vue-cli-plugin-webpack-bundle-analyzer": "^4.0.0", - "vue-template-compiler": "^2.6.14", - "vuetify-loader": "^1.7.2" - }, - "eslintConfig": { - "root": true, - "env": { - "node": true - }, - "extends": [ - "plugin:vue/essential", - "eslint:recommended" - ], - "parserOptions": { - "parser": "babel-eslint" - }, - "rules": {} - }, - "prettier": { - "trailingComma": "es5", - "tabWidth": 2, - "semi": true, - "singleQuote": false, - "printWidth": 120 - }, - "browserslist": [ - "> 1%", - "last 2 versions", - "not dead" - ] -} \ No newline at end of file diff --git a/frontend.old/public/img/icons/android-chrome-192x192.png b/frontend.old/public/img/icons/android-chrome-192x192.png deleted file mode 100644 index 0569b776e427c61e635ed4866433fb23da39b609..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12734 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 diff --git a/frontend.old/public/img/icons/apple-touch-icon-180x180.png b/frontend.old/public/img/icons/apple-touch-icon-180x180.png deleted file mode 100644 index 12ee3c053603c8793b8d4ad2078de8ee191a1a07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13075 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∓ zc{$6_WnDe@(5<}m+I<8s{~M1PjQ?2Edb1_}I!)Ogmv3t2&309wC}V$G&|M|wWy`=h z`w6VH(U7_qF2KrP+^jilsgk*jF>%D?S_4njsr0f6P+b)&w)%sDuN#l>~T^^f=*@^yDH~aGV2}GmBiOR3vhJUzNkqeZ4SJG|Ga7*S%i_L zmpcIt4EM+K#hgK|7K--tz#PuP`=a}X!n^+LZ-vCAuRYYvgRt?B>DRmg-5m$1oQ8KI z9TC%+j(00DMO@tF5vPwM-qu#aHugmNa#q4|zAw#c{EKzr`|LuXjde9F5c&jDo2^y$ zpVqx55t$2B`?5sJ3HIA%F)#hIaMBO0A$hzrTxM2z2X>u>giu|58NutHJOK)$IEX9n zzG;ZJhtw;r->-S2@ShDT!Cqxd@p4|J*i@u^uFe;;A^S0{V$KYdv)VGL8`K$y@bZGS z7o=*>wFJM(&OdmITS(d;KD=QBLJ}(*Ugxn5X`;6d>@u!+1BS-foI4Y;&&-eeFEB=M z+K+}-LzOIk#u+o%wHn)!1$jQbUYHIJ0D?SomU_AS)8v{4;0nVQ1{Xk#465%r&2q{Y zj@omRYO2qUFDBhZVqQ!}`=?x>BAOT_q^K~zx_1HfABGmP?Xm#yBIslEG&C$8GGS}8 z*0F+6pVy28oWd^{I?cwgGT?r+QFC*ciOaV-unL6c>dgf~7$_g6n86fipeQL0UWQ53 zF-+6{o@+WG+63nX;}iJlZc)QMnR^1}9C{PCSwzdhi3QS4py_?t%~~RESx2h>h`7ND zoJxdS6SlO$V!!_G6aY^m$+-1j4FO@K|Kit>gDg7(v_g@>caEE! z0c2=LvLwn_R_*7kWVV4PkORwu57%XCXTw3{pM=K&{1jNX|eRkO#D) zxd>s2PIc84ykggup_|d3HQp>M5_)s|A3Ygm37J@53Kgofsc!&^*n>>4Ij15-;K!=- zSwF0L* z0o7GfMG^5`J(>J4JEy%d*=Q#kYsfrZ`62B_LOeD2JzD|fi1RrBf@25SYiq6b+9!n= z!K8rN^IY=_Jw(pJHilNxUkq$0RaYxX+9p?9mH$>8BxIX{5bMn(#4@(M=MDE2WihOP zF~`%HUCiVVfNB9gx4(@2C4RcZLA1?*Hn*jdF(`QvNCT|r?Kzb*XYneuO@NqKr)M2F z5!fbBDrP;xVihgwu;wQTS<^uhsa!F1P|7u1Hk&UOqO3}NFi*RQv+RXwXHc~$djl1X0GzbZOH zQ<(J1nwD?`;i--6G?gJN)|D|%q_6In?-x^u6pX>?_UE+{Nr=EZT9t6(r;nLq997aQ z%ehS=0EHhGt3N~F3@-4Z8O_gV`4q^7!3Ss)m!au@Vo@iuOYUgEJ~P@tm-Q(agwq9! zHc1c_Q`%!|;Gd^+I!Qkp^yTqKM9SP397rSwCPN8xUmh8({rkR0=^$+6A zFWxEc)^c*1n?9~^(J-bh4_zHq83c1b=ax=^iR{~EWkzK90=Pyoy|Q!^;qfVfe|gFb z*qno@1XB>FVkDLe@pc-4#$=lParukL;knsOM|_qB&A>$G3PvBpS!)YQ&y8HUEcLDGAG@n!3LBbLMsdF- z;qdA~;uP?c4Dj?~F3zsb_H1laXk=2=XmnN!zu8wz{*N#P0O=MSe4N4NUmw5fU#|db zn$%i!QBx1$061&Q0F66=ZGQvZ&zs7;)SSnNF6h+F39>xF+5s)P@em|%zq(YAXKWId zxz)Ovm2fG7ye+gJIv#KUU9+lQ6IvW1=HBnHcpb zk37-_QN;gW#HIdkpnK``+K=#?kQX{koSMk)c7mN(N{6J{=)o0BV-Uk?K@Yj96|Yj9 zIf3K$3&e=lE&adqQ9&!*O7=F&CMg3yj7`Em)ora*j`BD&h)e)|^9u%H{Y`=2DwTKx zY(ER*!iq3?yh$kFD4KfTYZF3#vLWraRwR}B(^=y%;FO&d{QoR~=mfm_zhsd2rK%Kr R{kP=;kd;)Fs1h>_`adSpODzBZ diff --git a/frontend.old/public/img/icons/apple-touch-icon-60x60.png b/frontend.old/public/img/icons/apple-touch-icon-60x60.png deleted file mode 100644 index 772375179bb6a09645b351d11a9af7131f84d81a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2881 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 IM6N<$f@l*|_5c6? diff --git a/frontend.old/public/img/icons/apple-touch-icon.png b/frontend.old/public/img/icons/apple-touch-icon.png deleted file mode 100644 index 12ee3c053603c8793b8d4ad2078de8ee191a1a07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13075 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∓ zc{$6_WnDe@(5<}m+I<8s{~M1PjQ?2Edb1_}I!)Ogmv3t2&309wC}V$G&|M|wWy`=h z`w6VH(U7_qF2KrP+^jilsgk*jF>%D?S_4njsr0f6P+b)&w)%sDuN#l>~T^^f=*@^yDH~aGV2}GmBiOR3vhJUzNkqeZ4SJG|Ga7*S%i_L zmpcIt4EM+K#hgK|7K--tz#PuP`=a}X!n^+LZ-vCAuRYYvgRt?B>DRmg-5m$1oQ8KI z9TC%+j(00DMO@tF5vPwM-qu#aHugmNa#q4|zAw#c{EKzr`|LuXjde9F5c&jDo2^y$ zpVqx55t$2B`?5sJ3HIA%F)#hIaMBO0A$hzrTxM2z2X>u>giu|58NutHJOK)$IEX9n zzG;ZJhtw;r->-S2@ShDT!Cqxd@p4|J*i@u^uFe;;A^S0{V$KYdv)VGL8`K$y@bZGS z7o=*>wFJM(&OdmITS(d;KD=QBLJ}(*Ugxn5X`;6d>@u!+1BS-foI4Y;&&-eeFEB=M z+K+}-LzOIk#u+o%wHn)!1$jQbUYHIJ0D?SomU_AS)8v{4;0nVQ1{Xk#465%r&2q{Y zj@omRYO2qUFDBhZVqQ!}`=?x>BAOT_q^K~zx_1HfABGmP?Xm#yBIslEG&C$8GGS}8 z*0F+6pVy28oWd^{I?cwgGT?r+QFC*ciOaV-unL6c>dgf~7$_g6n86fipeQL0UWQ53 zF-+6{o@+WG+63nX;}iJlZc)QMnR^1}9C{PCSwzdhi3QS4py_?t%~~RESx2h>h`7ND zoJxdS6SlO$V!!_G6aY^m$+-1j4FO@K|Kit>gDg7(v_g@>caEE! z0c2=LvLwn_R_*7kWVV4PkORwu57%XCXTw3{pM=K&{1jNX|eRkO#D) zxd>s2PIc84ykggup_|d3HQp>M5_)s|A3Ygm37J@53Kgofsc!&^*n>>4Ij15-;K!=- zSwF0L* z0o7GfMG^5`J(>J4JEy%d*=Q#kYsfrZ`62B_LOeD2JzD|fi1RrBf@25SYiq6b+9!n= z!K8rN^IY=_Jw(pJHilNxUkq$0RaYxX+9p?9mH$>8BxIX{5bMn(#4@(M=MDE2WihOP zF~`%HUCiVVfNB9gx4(@2C4RcZLA1?*Hn*jdF(`QvNCT|r?Kzb*XYneuO@NqKr)M2F z5!fbBDrP;xVihgwu;wQTS<^uhsa!F1P|7u1Hk&UOqO3}NFi*RQv+RXwXHc~$djl1X0GzbZOH zQ<(J1nwD?`;i--6G?gJN)|D|%q_6In?-x^u6pX>?_UE+{Nr=EZT9t6(r;nLq997aQ z%ehS=0EHhGt3N~F3@-4Z8O_gV`4q^7!3Ss)m!au@Vo@iuOYUgEJ~P@tm-Q(agwq9! zHc1c_Q`%!|;Gd^+I!Qkp^yTqKM9SP397rSwCPN8xUmh8({rkR0=^$+6A zFWxEc)^c*1n?9~^(J-bh4_zHq83c1b=ax=^iR{~EWkzK90=Pyoy|Q!^;qfVfe|gFb z*qno@1XB>FVkDLe@pc-4#$=lParukL;knsOM|_qB&A>$G3PvBpS!)YQ&y8HUEcLDGAG@n!3LBbLMsdF- z;qdA~;uP?c4Dj?~F3zsb_H1laXk=2=XmnN!zu8wz{*N#P0O=MSe4N4NUmw5fU#|db zn$%i!QBx1$061&Q0F66=ZGQvZ&zs7;)SSnNF6h+F39>xF+5s)P@em|%zq(YAXKWId zxz)Ovm2fG7ye+gJIv#KUU9+lQ6IvW1=HBnHcpb zk37-_QN;gW#HIdkpnK``+K=#?kQX{koSMk)c7mN(N{6J{=)o0BV-Uk?K@Yj96|Yj9 zIf3K$3&e=lE&adqQ9&!*O7=F&CMg3yj7`Em)ora*j`BD&h)e)|^9u%H{Y`=2DwTKx zY(ER*!iq3?yh$kFD4KfTYZF3#vLWraRwR}B(^=y%;FO&d{QoR~=mfm_zhsd2rK%Kr R{kP=;kd;)Fs1h>_`adSpODzBZ diff --git a/frontend.old/public/img/icons/favicon-16x16.png b/frontend.old/public/img/icons/favicon-16x16.png deleted file mode 100644 index 05e2e7625d463ecd7868229db7514e6bc416ccb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574 zcmV-E0>S->P)a}rVZ8sE49+v z(!$ogv-t_)?@$m-5DX>=0SmPiB$&BEet>8puFv_H?25(9x##PhGw(f9q!%(MhW0Q($YK z;41zB{))vz&j~bng~SZJNt4`+jS4yGGBuw$><;z`qu-YCA+GZ%8{jvrGwd!!wZ(PR zOW>H?W8#k>-o@V}w=IVk3x!9p&Irz6-lMSZcYQ`)V&CvzQge-(PvjrUq|#iekg*wH zzxosqh4-$_eXRIgF?_)Pi2uN~tNirfcMSuGz6c2??crY_{(|D4#D3Ux;jT8v&=XtC z)kaknCalx=9QO0zm5dK=k-Lt6!v(JMPM8KpAm4raD>~j=V!voU1+VQ_>a>07IoWBP zV7@eZjcz)S?*r2!jA&Z(Dc+^&sGMKfHrSeFO{ICVDq`+?!#Ec~G6sM6-tQ*EtXr80 zN}pgElEWm0EzPY7CEB=X<76aKvz$qag>Bo?`?ld`7S;A&(es84v^WJ@dF55S?{VlN zh9zI^%J+ZqQB~fwl%`DdbeJ`;XM57-`|=v2H{0m{zto+PJwS4Q0RmjYjGF}PUjP6A M07*qoM6N<$f)Vr+4gdfE diff --git a/frontend.old/public/img/icons/favicon-32x32.png b/frontend.old/public/img/icons/favicon-32x32.png deleted file mode 100644 index 9263cc67c9c480597e617ed2bb38487b6eb9690a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1217 zcmV;y1U~zTP)dHa_+rn=FQr>YaA#LD_zaLH#7H~ zxu5fvmi}+Q7?*#!R5#_mEp2J!f+oGhS zm$8>g$h)(_U6HN!^3vD{JgItxgz22Es}QgS7jTkGM#S~Yi58Hk!2#McUH&FyA39#7 zI|kV`VyROE5;i7T(7KN>{tCcVnvff3yF*EL;C%ydH=ttz;=g15njWH#jgtft{exTUoaKDz_m{a#uA;HNTR>Mz?gjj+x(UJHK%C1#e@d2%HU~9z+ zn#vRI+ts>ahZ~gCt?Nqa^10CnH4>XCX9fM6%rZjYB`O+0eoh24Yq>+r|7mgHBgCgF zfQ_`DRgz0HX_9s_jfu@@L3yYOt-w6bEVvBWVbznlyHTuTCYR?eh%Xhj?fT99c3Ma{ zB|*1*%*?olxvR|MQPTWo`nB$x^qHC!^ZpCWeS-HFt@`=?Ota4?kz+!FawTv45bs`2 zHMqIs0PBvGSliWl)1jvB&22(ww%6cu!Lk2lzKsHIWqZN^jF5E(=3`P_Euekbd#+!1 zq0r3(ntccv^W*~hHRLM`RDG7YHD<=3>lyx0z{`zhz`KIv@DB4`z`nBrrT};13=+~i z&P}4!`O=0p9-z>TQ+M(^V_dIdSS>9Y!!80}snNReL$I-yAUWXb~Wac*WiKRQ#W9hGf5;~L>3UR7R$@S zcJX;__6#1Tzzd03k*H#38#2llk(mT!at}c@HW%E@<+Teq^LY(ke-Cu->qx{*!ZPQ_ zx~|Miy@r>ED_VAg{~?8y?&R}swI@i_?!embG2Vkbm-1CCxP^tMVIV$>%}>PkwAmMZ z8J%>8=m6j{?*Yx+W)!DOZ!fpa*z%Hc2KaOzDeh=Gca-3s-wo^XYixbpq95TKv3%w= f9k~B*eNF!b$F^GVe3Z1V00000NkvXXu0mjf_mW7~ diff --git a/frontend.old/public/img/icons/msapplication-icon-144x144.png b/frontend.old/public/img/icons/msapplication-icon-144x144.png deleted file mode 100644 index c4a5fcb71b59900301f6a3137f4b61e7e094cc77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8950 zcma)Cyx6&XnQc94~2nYj2TE2e% zg5U4Oi5KU&uXC>J#Ea*S)z(xdCZHz(006|QDlpxDHswFT!}+K8m9{$m8GLsYV^09^ zJ>Wk9?qO^S001liRhXQ<@9HmeQ)k8{c>a?mjd-(E>PFsp%J=(|{9Ips%tuw`Wkh=m zfww6bfsWu+PB{}%4OE9u-g48>dfv(t?^xoSpz0i)r$i8Y(5I&+g@Hd6W~o&f`WkK2 znuaEj|AAzGN=!trKKdXhd9!!YR!g%M7+*K=jFlnn^d)+Ae`)PA=MTdav615RB_ekz^B!gvUn)Dt5R@uf%JAa{7CmQ z21wS`)_Vg&l`(wl`Bt&pzsBw4{jGQ+dOfr5&(WRMV;q$Mr5Rqn{GQ#Ytn(Dvm4_yV zk-?@!-N4cIWtGdbq$G?O{ix1aFs2SBSYF$^#@H=EL+~W4YFxBRLa-ms^MO`lcCN`l zt)P&Yrc2B?J2P4afcm<5A(hyN54FSpA%J0zen`J);1bAk!Ab_|;PtIuI)NYFCjzky zISbc!W~TbXY4N0eYx@$*u>>)u+xx5sFYHKS`^UdCc{^rjxA zTo`fcN86>IoiaBcmg)ppzNLvo>G0r@W03$N`vzq(t0Shrd1_u;AkUMNLlgy!HVeSZ z!$UXvKR$XZpM?_LGFtq_mceV*O92RfKq_Q-u)xxf`h!h9YY?$p-q>sjO5t%JO8_YV zLecR393!bMi%#^~qHlXnWT=2oC?!|VJHczBSbxx#=nJ$*4Ngj6ay5rJ79+tFcElG; z>kiSui(?_6h0!oj-LZ5^EqyJfHMoj1XwLn@-^ad|(nrx@L&rQUr`1Uh9fM>1Y6HEv zX9U6=RXe6t*L-O-ca2HYyp#_PH`G#dsbsv;sqViqWnlbtyTWD;*yC@Id20vY-+6AC z9o@*X=NtKzS%&hOE@!JUDuj zjw?P}Q5A~Q$#EIsV^}2CdpzL)Np&k{vX!6Zat~FQ%*o|7;Q&x6dihVdtkkS59IJ01 z*yEIF0p^9^I6OoC#aBGm{bwr<0|6tW`*)oQMFiLToricH(_Qg7wBoFLW3MT?k%-Qh zR-kz{bPx;MaUjm8J(%Xs`!(XjY!uKe2@J!4zT?_nVh^k3zs(#_d72f2FV@jA_XEU` z$WysW_js_WuFa|Dp?(sm7$Fa@7ylq!%-SOpX5e>O5S%>_xFv+4~^F4TnwUuM{1&uAJuQ(Vq9grOyW@kh=bdP`U8#+ob zj8orRzze7taglm#?r#vLmErcYPYweO4+b_00=EYHwBm7K*YK2xEBsJ?6tb8SJD`JR zM?nC`rB=&}PIlVyC=7JWb$t55JZx!Qy1t9@)Q`T#OoI+y@Xse*XKC~2x%j=y`2n~$;59F5{lA!VS8Z7BJW$r>H> zSW(Bce4t4>Ox4g=HbzI0f`7x)Cvi6x5W$5*Zg47+TFHQ^MTsEyHH^%&X*71N}zaupoGrQ19kd1XLh8 zFI;&IAuO3{P_ub8dHpt1;anwaY?o>Ilx5CU$pSaP$L8vsMwQ?6ovHi>VqZ}!pL`hK zr~oq?a?pDQ+vzWY6`(om4!#w4Br(Zf+Wwf+A-~dR(o?!&O3H)$MTzg{Qzjv^D&Fm) z%{ya$E|`U(<%0jB$nMQ8)k-KKJq&%ps7xaSx~!BUw8<(DWr*jS?&T!z&zX2SX|jhG z(+uj%|2-jbl$I?nnY;-djhlp~myFRTj5#P$AG{+YX4}F38%tJgO#e#+t-=lb;b^r|8=z=U zsSNIz*KBUzAklaGH$Oo#Lt79rgxaPUuQsfPLdsrezyw3*HwXYLzcA3&eXxN~zM@U} zlhbYUwqnAGyyTucuJ__NfbOKyKya4Y$6WMF6v8LA?e&HBYn|rk8JEZ?!5n>MMkrfi zg=9~=gfdP91L1>ap|+YXzPZejDJhOl5R$-0cqrMpFx}88bZf|OYVZZ9GbvHLN-mtVuK zc&-_`Ect;`eI-1CtfIE1OL~@+Rw+9PMlxzN;k6NaK?{~8&psf$wlaxOZ9iz5p}Fit z5IpEUMTBwngR5}dlM|zU0M(>HSFcy$$!iv;;aw8g*&tA22(B^7trvCzOKUd zvGyEM`At(F1*?aM9b!4N7zeOLP?ch2`>0dnw+34;5U5F+5|HaNVHQR?k*WmF(_o`> zjgxi7s~dkKn*wg8ad&l4>Ks^0^BCZ_=W(gU0@I`3w)b%&Y(GnR9lfS> zlVr+&zKaY#_22kvl1QF2fVcT^NNh3p6Fc>NqmK1b5*V-1JtP zD3?t2Ew*sD^5Ew;*Pxtfg#L}?bta)z#$Y_5GHHDLnT8^gE|$Z-GHy; zSI@sA%4)+u)6^R%j00@^swp|^B82#gb6AA4#^#6grf&0H;-3KC8+=NOvQ3VCR)%RH z6q_*1vYUkkb;kB$x;Ql9sp`xC6Vat(-|GFq~op9O2UMpYoxKMz*3d|yv*J>-f}q1gHidlv}ORKEA4rii#qM=Smo1Tws263^T| zNB*_hlq$P@*E>ZaXqehWydNpfjC19ku<$38C2prlSlucZWSk$_F_5c&BJrT$M<>pa zW?HXBt=j6UAHUV5zc-M5cELPwl{~(0Y<=#MUvK2Q&&6D81W$lX`UwE__wz(%P&t32 z0vd)Jx~ZS;A-#&I;@`M$qma%|X0_W$W`~@;v3x8WaqA|OF2!{>%=ec>Ly6gsnwm~< zV#dX(6|eoz&f}Tq0|+8V<&bcyhBK72y%+f5arC7Xv~+lo>)LFvixQ`rt_;XwWP@Cc zS)ct5iK2zp`H7;|wsm)e7=fQ$>8yF+aO*GLl|F^-)D^}IiP7f;N#8Dgz9%Xg2LN?I zztov|Q^}bNYW*dg#+gneW9Uf_1+Z04hKd+a52QO*0(HC4qrC^O-$VR^dK*ETGK8Ni zOgY}$o<<8?Dr2-o#*kN3&?a+B{yZ)3Kim=BmqWjeO;92DKh%CH2sI)LDdI~&JG-p<+VW4`& zVtKl{Vl3cho;zmhX?`q4s3z9QU*+&8cRK#aWFf1Oy_#p8dJ9SYq~+0-BGg#t4z*t& zE7Ovqzi$(Yh3UpEnyaiM4a#cKeN~aYRLX%s*od`%v|-CMx8rE$7*^(9&E5#aiEUW` zT~fbzj>T0-ZzkA(JOWNi?as^(i&>YW%p-&7CV{^5R@MB)k_kBhIjNV3Rw*pbHS5=% zF{ip$8MN;;y-Ijb`|PFwWw$}o-U7w&h{?$r$R zcD8D|o@^|^?lxBUCk;UEIbczc9gOaFa^N+W(VU<1s#fh5fnGen>SVwVk^It*=_m?X zs;AcJrv2n+X(-JFFu`l@k!r6qQpPq;a;3unRTd+GtRZ_^$>z^u8pKquM+&^yS2NrL zUPaG$X+Dj4QJHxi$wC5NUr}34O~ihiC4lhXfu08ZDgk}oPuY=WU0u+fd*$Gf_2bB* zS&ta7#X!2}`!ou#RH{iEpa9r(58*^&z55mD9-iG2OKM8Fa$*@=Z%YytJ=vF)Z2CtN0V=oR4VsmKdAQ z*{g)*!}8Q_d3Wv2jV0VCb!EbCfJ8Gs(3{GI zO}Q8_gut}1$uD<}iOfw$np{0L$DZcTdh^DF`wHHZ`pkT$JVgGO@f5-3lI*ZmN8P9{ z{_HEsw_e#t5y>}o^H{k=;*oz;Yi}|=z*aQxgzj0KU`Ai6`6FU`lIHb5t@@7+wWat= ztd-_Sa?1|4Pc|T(O)3H>s!LCIQUQkiS;C2`XjO-|%if=vl9&svMSwcCiwuqYvqrR+ z>TIho!%PiTc-1S&UKyfzk~Az5FpRYn=@JTw|@FFVuc} z_OX-1^Nh1)_npUT`i(Ry=x>nTpVY^FtS$}D1b0}cTYyqe6`g+}dV?qZ?r34X3#9e= zyJ$Ad+&NQEANlFqIaNX`t=t1S!WjIY?mHE zJwB6c#I&?salcWg^Ugp9T`KOQ3&OEZ%gU~5{5X!C;;Hov@dLQJ3^nS}uyBNL{p*j6 zB%!He6df#b5U56KoWL^8Eag1qB3O}T67KZrge30e9qyTJzlBk;3LvBIm8Lf|+`3kU znp2-Noo!PX(uxlq4ZJ?qYgIrf4I^hV*4E$si26Ngsx*ia);=!lnVAb}QecVm7U@7C zdj@DJ=H#hI7jpes+Ffr3Zj^O`xr}6M0|9)CA=5kF(8c1C(YeczX}W$dMHLg4$fe;2TgGmR%Hf#o?J_wnpxyfF2m9i>v?Cf6y zoIHFu9rf1~_8krwgmcIEi0D9__`_Z0t+20FB*^c8!emE4s|m|-m<#Il1io^gRu-il zorbd2@1&3~rO!1wQh0{%U&HNRR%bI87W;B7Z0N=F-0G9VELa_4*NnvNw9@Oa@_En= zZmArCoedq>!YBks`vk(>FcAwQpJh>~5wr56L7u%a&0>E6bDwcSq6t#{5>LCE`&YlG zDo22(lHE5vm`mK(?kaqQ1-F)Y=+gI3Jl3Fo3dk&8K00DB3xc7Q)JLJUO3&7f+2?>w zYT8#nbKPBK4}A(M{@j|arL>gEr(Zdngn=62 z2Ztpht>p7+UKR;`6eIL!b-BxK#R4@#BVDYH+baNQ{|G9!+;I|B`^yc7LYhC`fAs3= z^)qWGsW;V0Q04Dkb=2Wwv#c_q6*^Bqc^V=1{zHCG7Q0LPFR0DltJtgQo!dc3^iDLx zai@WE1PwOCE_BFD4?7` z9vj7o^n*sByG^Sv^oRJAb@L7sXO|1 z!O#@)@=%S|Fgx~kDyQ$Wr920JND$jRU)c_u1r~pCnZ`cX5`7^jHfSF3RG@&Quv>_~ zfY)T{VGiF2m>e?JDe*#cN21YFY!Nrr0rHYl3n6&nURq;mu=qXLzZ^)4EOtF8kt~^e z<-C@onZ)`d;sWM9+EwlK5fXb4CA;4d5;tLDX2)e9Wbe0OW6q$~_>%+rxdH2>96!Rp0pVi@4f{&9806!`s}=9 zC}ujXM4irQ?}H1XHasS(GX>^@l_8>-`-sSmP7{CG$iwRGFR5Xl(t8lD;YRmtC5@x?4h&b^EBiZa9!2HM@K7veHg<+&j|7e(A%~a?Vc@|pl4{zpM#D}85URwuwX??IyApH zha{;O>T7oqMwsAZ9x3J)by7kx&u6M=fL*bVQz~GxutrX&uaWcTHx~RjO*Losyb2SQ z;Sm|&^lL`w=C=;MHYh%t7Vz(Gy|-gZ5^^o4D7D^hH6n(Z6m{W1aaThj2N8l0Gqs(z z19vL!2!S?DYAdxCl9;e+at1xk1yl3_9&g4`aatlCv_9HtEo-%ES2b*O3NrGLgPiV= zq5!oj*?p&B(6e5bYLASL%b?rv1uY6$M=c3wK` zTGvI32y)x!q-_yfXOg^?eX(+RSsVovv2Y;FUn<3O7COl2aUFs8+-D^{C$LFZTqQd~ z4v!GWdXLAp5re%<$$Xn?QK|%R$M0;LO1XB;R54EoY;vDcG2EpH68ior`MyfvD@lgm zPMCigW<|5&$160g+Dbkx4#ZT*SJ`zbt4X zRyw(n&E}~MIV>-SN1PgLAr=mgHbZNm$3MLiR7$mBXTzQ0whZet?^C85&i0GyU# zv|D}J=W9+xQ959b~^f77}f8>&1_YqoLz+lb?M@m~&g;dXdy2B~F+Lw(`t zd=?sQc$R@rHd}GDxW#3b^f zUnJ8|&>WL|Cl2y5Ya3akd~PlMa-A;plRFDnOP>9evfa+IS%ZH?;rSAf`Oci{1E6&{ zT6P~*WMd-!Arbe97>c0iDA8+pr2EQUZcfcb*bkilptecE%5|^@)Wb=LeE%q-ghn-d z3pwxzZbc6D;Sq36~4QxC|?`5;x9HIx`;l z>m~vQLbGK8P+irG9XE!>Njic0dbq9=8tPx4H)QuW=P!?Cjjg0fnJPmh370B5#?>t` z#~||YHg^s)^X57mS#+?KKYL4+`x5*9!eUE${$Ur+iq1qWCZ}9qDcTNTzvNoT4@#zP z5Y6AN7NptWL@2t3z;2}Mb1X|iFuU<2TEeV~QHnQfn$*P~xx|?}y52jx=rcSb_!B8f z$#Cg_#L9p26H)yQ;)&`~5+;WJa6H$3{BiGe#hfyK<9sx3!@M)^2;FrAMdHBrMx-#O z!QQ5MN_M}Z%xYN|T}{#Ix2-*wBbMMs`nS>CvJYR`AC5=qpj$q|a&JW`(6LhAUKBtX z^LSPpRSqnpm8EWPcSbaaM*g5AVo``7U)u6P+XEuf+-1wwyTB`SHrNrAIVx-&OB7=b zVK_ZpTWqCfuwii|%#Tk7gV0Ygw3`h0z3^Xf$5{*oYs~$rk=L3{%xc6@!pHQ#x@c!D=iiLj!Jlaq zF56FK(pV6XyQo%KwNg098dzSe^Xqu>S%7-UAK* diff --git a/frontend.old/public/img/icons/mstile-150x150.png b/frontend.old/public/img/icons/mstile-150x150.png deleted file mode 100644 index c6dfc5414f9b7d0267590fc742e4140fcfe5f3db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10304 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 - - \ No newline at end of file diff --git a/frontend.old/public/index.html b/frontend.old/public/index.html deleted file mode 100644 index fa926f2b96ea..000000000000 --- a/frontend.old/public/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - Mealie - - - - -

- - - - \ No newline at end of file diff --git a/frontend.old/src/App.vue b/frontend.old/src/App.vue deleted file mode 100644 index 63d1c45e1549..000000000000 --- a/frontend.old/src/App.vue +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - diff --git a/frontend.old/src/api/about.js b/frontend.old/src/api/about.js deleted file mode 100644 index 92434fad1311..000000000000 --- a/frontend.old/src/api/about.js +++ /dev/null @@ -1,80 +0,0 @@ -import { apiReq } from "./api-utils"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -export const aboutAPI = { - async getEvents() { - const resposne = await apiReq.get(API_ROUTES.aboutEvents); - return resposne.data; - }, - async deleteEvent(id) { - const resposne = await apiReq.delete(API_ROUTES.aboutEventsId(id)); - return resposne.data; - }, - async deleteAllEvents() { - const resposne = await apiReq.delete(API_ROUTES.aboutEvents); - return resposne.data; - }, - - async allEventNotifications() { - const response = await apiReq.get(API_ROUTES.aboutEventsNotifications); - return response.data; - }, - - async createNotification(data) { - const response = await apiReq.post(API_ROUTES.aboutEventsNotifications, data); - return response.data; - }, - - async deleteNotification(id) { - const response = await apiReq.delete(API_ROUTES.aboutEventsNotificationsId(id)); - return response.data; - }, - async testNotificationByID(id) { - const response = await apiReq.post( - API_ROUTES.aboutEventsNotificationsTest, - { id: id }, - () => i18n.t("events.something-went-wrong"), - () => i18n.t("events.test-message-sent") - ); - return response.data; - }, - async testNotificationByURL(url) { - const response = await apiReq.post( - API_ROUTES.aboutEventsNotificationsTest, - { test_url: url }, - () => i18n.t("events.something-went-wrong"), - () => i18n.t("events.test-message-sent") - ); - return response.data; - }, - // async getAppInfo() { - // const response = await apiReq.get(aboutURLs.version); - // return response.data; - // }, - - // async getDebugInfo() { - // const response = await apiReq.get(aboutURLs.debug); - // return response.data; - // }, - - // async getLogText(num) { - // const response = await apiReq.get(aboutURLs.log(num)); - // return response.data; - // }, - - // async getLastJson() { - // const response = await apiReq.get(aboutURLs.lastRecipe); - // return response.data; - // }, - - // async getIsDemo() { - // const response = await apiReq.get(aboutURLs.demo); - // return response.data; - // }, - - // async getStatistics() { - // const response = await apiReq.get(aboutURLs.statistics); - // return response.data; - // }, -}; diff --git a/frontend.old/src/api/api-utils.js b/frontend.old/src/api/api-utils.js deleted file mode 100644 index 2a9b0acb4a85..000000000000 --- a/frontend.old/src/api/api-utils.js +++ /dev/null @@ -1,121 +0,0 @@ -import { prefix } from "./apiRoutes"; -import axios from "axios"; -import { store } from "../store"; -import { utils } from "@/utils"; - -axios.defaults.headers.common["Authorization"] = `Bearer ${store.getters.getToken}`; - -function handleError(error, getText) { - if (getText) { - utils.notify.error(getText(error.response)); - } - return false; -} -function handleResponse(response, getText) { - if (response && getText) { - const successText = getText(response); - utils.notify.success(successText); - } - return response; -} - -function defaultErrorText(response) { - return response.statusText; -} - -function defaultSuccessText(response) { - return response.statusText; -} - -const requests = { - /** - * - * @param {*} funcCall Callable Axios Function - * @param {*} url Destination url - * @param {*} data Request Data - * @param {*} getErrorText Error Text Function - * @param {*} getSuccessText Success Text Function - * @returns Object response - */ - unsafe: async function(funcCall, url, data, getErrorText = defaultErrorText, getSuccessText) { - const response = await funcCall(url, data).catch(function(error) { - handleError(error, getErrorText); - }); - return handleResponse(response, getSuccessText); - }, - /** - * - * @param {*} funcCall Callable Axios Function - * @param {*} url Destination url - * @param {*} data Request Data - * @param {*} getErrorText Error Text Function - * @param {*} getSuccessText Success Text Function - * @returns Array [response, error] - */ - safe: async function(funcCall, url, data, getErrorText = defaultErrorText, getSuccessText) { - const response = await funcCall(url, data).catch(function(error) { - handleError(error, getErrorText); - return [null, error]; - }); - return [handleResponse(response, getSuccessText), null]; - }, -}; - -const apiReq = { - get: async function(url, getErrorText = defaultErrorText) { - return axios.get(url).catch(function(error) { - handleError(error, getErrorText); - }); - }, - - getSafe: async function(url) { - let error = null; - const response = await axios.get(url).catch(e => { - error = e; - }); - return [response, error]; - }, - - post: async function(url, data, getErrorText = defaultErrorText, getSuccessText) { - return await requests.unsafe(axios.post, url, data, getErrorText, getSuccessText); - }, - - postSafe: async function(url, data, getErrorText = defaultErrorText, getSuccessText) { - return await requests.safe(axios.post, url, data, getErrorText, getSuccessText); - }, - - put: async function(url, data, getErrorText = defaultErrorText, getSuccessText) { - return await requests.unsafe(axios.put, url, data, getErrorText, getSuccessText); - }, - - putSafe: async function(url, data, getErrorText = defaultErrorText, getSuccessText) { - return await requests.safe(axios.put, url, data, getErrorText, getSuccessText); - }, - - patch: async function(url, data, getErrorText = defaultErrorText, getSuccessText) { - return await requests.unsafe(axios.patch, url, data, getErrorText, getSuccessText); - }, - - patchSafe: async function(url, data, getErrorText = defaultErrorText, getSuccessText) { - return await requests.safe(axios.patch, url, data, getErrorText, getSuccessText); - }, - - delete: async function(url, data, getErrorText = defaultErrorText, getSuccessText = defaultSuccessText) { - return await requests.unsafe(axios.delete, url, data, getErrorText, getSuccessText); - }, - - deleteSafe: async function(url, data, getErrorText = defaultErrorText, getSuccessText = defaultSuccessText) { - return await requests.unsafe(axios.delete, url, data, getErrorText, getSuccessText); - }, - - download: async function(url) { - const response = await this.get(url); - const token = response.data.fileToken; - - const tokenURL = prefix + "/utils/download?token=" + token; - window.open(tokenURL, "_blank"); - return response.data; - }, -}; - -export { apiReq }; diff --git a/frontend.old/src/api/apiRoutes.js b/frontend.old/src/api/apiRoutes.js deleted file mode 100644 index 5d726267743f..000000000000 --- a/frontend.old/src/api/apiRoutes.js +++ /dev/null @@ -1,90 +0,0 @@ -// This Content is Auto Generated -export const prefix = "/api"; -export const API_ROUTES = { - aboutEvents: `${prefix}/about/events`, - aboutEventsNotifications: `${prefix}/about/events/notifications`, - aboutEventsNotificationsTest: `${prefix}/about/events/notifications/test`, - aboutRecipesDefaults: `${prefix}/about/recipes/defaults`, - authRefresh: `${prefix}/auth/refresh`, - authToken: `${prefix}/auth/token`, - authTokenLong: `${prefix}/auth/token/long`, - backupsAvailable: `${prefix}/backups/available`, - backupsExportDatabase: `${prefix}/backups/export/database`, - backupsUpload: `${prefix}/backups/upload`, - categories: `${prefix}/categories`, - categoriesEmpty: `${prefix}/categories/empty`, - debug: `${prefix}/debug`, - debugLastRecipeJson: `${prefix}/debug/last-recipe-json`, - debugLog: `${prefix}/debug/log`, - debugStatistics: `${prefix}/debug/statistics`, - debugVersion: `${prefix}/debug/version`, - groups: `${prefix}/groups`, - groupsSelf: `${prefix}/groups/self`, - mealPlansAll: `${prefix}/meal-plans/all`, - mealPlansCreate: `${prefix}/meal-plans/create`, - mealPlansThisWeek: `${prefix}/meal-plans/this-week`, - mealPlansToday: `${prefix}/meal-plans/today`, - mealPlansTodayImage: `${prefix}/meal-plans/today/image`, - migrations: `${prefix}/migrations`, - recipesCategory: `${prefix}/recipes/category`, - recipesCreate: `${prefix}/recipes/create`, - recipesCreateFromZip: `${prefix}/recipes/create-from-zip`, - recipesCreateUrl: `${prefix}/recipes/create-url`, - recipesSummary: `${prefix}/recipes/summary`, - recipesSummaryUncategorized: `${prefix}/recipes/summary/uncategorized`, - recipesSummaryUntagged: `${prefix}/recipes/summary/untagged`, - recipesTag: `${prefix}/recipes/tag`, - recipesTestScrapeUrl: `${prefix}/recipes/test-scrape-url`, - shoppingLists: `${prefix}/shopping-lists`, - siteSettings: `${prefix}/site-settings`, - siteSettingsCustomPages: `${prefix}/site-settings/custom-pages`, - siteSettingsWebhooksTest: `${prefix}/site-settings/webhooks/test`, - tags: `${prefix}/tags`, - tagsEmpty: `${prefix}/tags/empty`, - themes: `${prefix}/themes`, - themesCreate: `${prefix}/themes/create`, - users: `${prefix}/users`, - usersApiTokens: `${prefix}/users/api-tokens`, - usersSelf: `${prefix}/users/self`, - usersSignUps: `${prefix}/users/sign-ups`, - utilsDownload: `${prefix}/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}`, - mealPlansId: id => `${prefix}/meal-plans/${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`, - recipesRecipeSlugZip: recipe_slug => `${prefix}/recipes/${recipe_slug}/zip`, - recipesSlugComments: slug => `${prefix}/recipes/${slug}/comments`, - recipesSlugCommentsId: (slug, id) => `${prefix}/recipes/${slug}/comments/${id}`, - 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/api-tokens/${token_id}`, - usersId: id => `${prefix}/users/${id}`, - usersIdFavorites: id => `${prefix}/users/${id}/favorites`, - usersIdFavoritesSlug: (id, slug) => `${prefix}/users/${id}/favorites/${slug}`, - 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}`, -}; diff --git a/frontend.old/src/api/backup.js b/frontend.old/src/api/backup.js deleted file mode 100644 index 31c354c07e9d..000000000000 --- a/frontend.old/src/api/backup.js +++ /dev/null @@ -1,62 +0,0 @@ -import { apiReq } from "./api-utils"; -import { store } from "@/store"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -export const backupAPI = { - /** - * Request all backups available on the server - * @returns {Array} List of Available Backups - */ - async requestAvailable() { - let response = await apiReq.get(API_ROUTES.backupsAvailable); - return response.data; - }, - /** - * Calls for importing a file on the server - * @param {string} fileName - * @param {object} data - * @returns A report containing status of imported items - */ - async import(fileName, data) { - let response = await apiReq.post(API_ROUTES.backupsFileNameImport(fileName), data); - store.dispatch("requestRecentRecipes"); - return response; - }, - /** - * Removes a file from the server - * @param {string} fileName - */ - async delete(fileName) { - return apiReq.delete( - API_ROUTES.backupsFileNameDelete(fileName), - null, - () => i18n.t("settings.backup.unable-to-delete-backup"), - () => i18n.t("settings.backup.backup-deleted") - ); - }, - /** - * Creates a backup on the serve given a set of options - * @param {object} data - * @returns - */ - async create(options) { - return apiReq.post( - API_ROUTES.backupsExportDatabase, - options, - () => i18n.t("settings.backup.error-creating-backup-see-log-file"), - response => { - return i18n.t("settings.backup.backup-created-at-response-export_path", { path: response.data.export_path }); - } - ); - }, - /** - * Downloads a file from the server. I don't actually think this is used? - * @param {string} fileName - * @returns Download URL - */ - async download(fileName) { - const url = API_ROUTES.backupsFileNameDownload(fileName); - apiReq.download(url); - }, -}; diff --git a/frontend.old/src/api/category.js b/frontend.old/src/api/category.js deleted file mode 100644 index d06d4b5b3932..000000000000 --- a/frontend.old/src/api/category.js +++ /dev/null @@ -1,111 +0,0 @@ -import { apiReq } from "./api-utils"; -import { store } from "@/store"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -export const categoryAPI = { - async getAll() { - let response = await apiReq.get(API_ROUTES.categories); - return response.data; - }, - async getEmpty() { - let response = await apiReq.get(API_ROUTES.categoriesEmpty); - return response.data; - }, - async create(name) { - const response = await apiReq.post( - API_ROUTES.categories, - { name: name }, - () => i18n.t("category.category-creation-failed"), - () => i18n.t("category.category-created") - ); - if (response) { - store.dispatch("requestCategories"); - return response.data; - } - }, - async getRecipesInCategory(category) { - let response = await apiReq.get(API_ROUTES.categoriesCategory(category)); - return response.data; - }, - async update(name, newName, overrideRequest = false) { - const response = await apiReq.put( - API_ROUTES.categoriesCategory(name), - { name: newName }, - () => i18n.t("category.category-update-failed"), - () => i18n.t("category.category-updated") - ); - if (response && !overrideRequest) { - store.dispatch("requestCategories"); - return response.data; - } - }, - async delete(category, overrideRequest = false) { - const response = await apiReq.delete( - API_ROUTES.categoriesCategory(category), - null, - () => i18n.t("category.category-deletion-failed"), - () => i18n.t("category.category-deleted") - ); - if (response && !overrideRequest) { - store.dispatch("requestCategories"); - } - return response; - }, -}; - -export const tagAPI = { - async getAll() { - let response = await apiReq.get(API_ROUTES.tags); - return response.data; - }, - async getEmpty() { - let response = await apiReq.get(API_ROUTES.tagsEmpty); - return response.data; - }, - async create(name) { - const response = await apiReq.post( - API_ROUTES.tags, - { name: name }, - () => i18n.t("tag.tag-creation-failed"), - () => i18n.t("tag.tag-created") - ); - if (response) { - store.dispatch("requestTags"); - return response.data; - } - }, - async getRecipesInTag(tag) { - let response = await apiReq.get(API_ROUTES.tagsTag(tag)); - return response.data; - }, - async update(name, newName, overrideRequest = false) { - const response = await apiReq.put( - API_ROUTES.tagsTag(name), - { name: newName }, - () => i18n.t("tag.tag-update-failed"), - () => i18n.t("tag.tag-updated") - ); - - if (response) { - if (!overrideRequest) { - store.dispatch("requestTags"); - } - return response.data; - } - }, - async delete(tag, overrideRequest = false) { - const response = await apiReq.delete( - API_ROUTES.tagsTag(tag), - null, - () => i18n.t("tag.tag-deletion-failed"), - () => i18n.t("tag.tag-deleted") - ); - if (response) { - if (!overrideRequest) { - store.dispatch("requestTags"); - } - return response.data; - } - }, -}; diff --git a/frontend.old/src/api/groups.js b/frontend.old/src/api/groups.js deleted file mode 100644 index 62ad406b3b43..000000000000 --- a/frontend.old/src/api/groups.js +++ /dev/null @@ -1,53 +0,0 @@ -import { apiReq } from "./api-utils"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -function deleteErrorText(response) { - switch (response.data.detail) { - case "GROUP_WITH_USERS": - return i18n.t("group.cannot-delete-group-with-users"); - - case "GROUP_NOT_FOUND": - return i18n.t("group.group-not-found"); - - case "DEFAULT_GROUP": - return i18n.t("group.cannot-delete-default-group"); - - default: - return i18n.t("group.group-deletion-failed"); - } -} - -export const groupAPI = { - async allGroups() { - let response = await apiReq.get(API_ROUTES.groups); - return response.data; - }, - create(name) { - return apiReq.post( - API_ROUTES.groups, - { name: name }, - () => i18n.t("group.user-group-creation-failed"), - () => i18n.t("group.user-group-created") - ); - }, - delete(id) { - return apiReq.delete(API_ROUTES.groupsId(id), null, deleteErrorText, function() { - return i18n.t("group.group-deleted"); - }); - }, - async current() { - const response = await apiReq.get(API_ROUTES.groupsSelf, null, null); - if (response) { - return response.data; - } - }, - update(data) { - return apiReq.put( - API_ROUTES.groupsId(data.id), - data, - () => i18n.t("group.error-updating-group"), - () => i18n.t("settings.group-settings-updated") - ); - }, -}; diff --git a/frontend.old/src/api/index.js b/frontend.old/src/api/index.js deleted file mode 100644 index dbc633212f33..000000000000 --- a/frontend.old/src/api/index.js +++ /dev/null @@ -1,37 +0,0 @@ -import { backupAPI } from "./backup"; -import { recipeAPI } from "./recipe"; -import { mealplanAPI } from "./mealplan"; -import { settingsAPI } from "./settings"; -import { themeAPI } from "./themes"; -import { migrationAPI } from "./migration"; -import { utilsAPI } from "./upload"; -import { categoryAPI, tagAPI } from "./category"; -import { metaAPI } from "./meta"; -import { userAPI } from "./users"; -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 - */ -export const api = { - recipes: recipeAPI, - siteSettings: siteSettingsAPI, - backups: backupAPI, - mealPlans: mealplanAPI, - settings: settingsAPI, - themes: themeAPI, - migrations: migrationAPI, - utils: utilsAPI, - categories: categoryAPI, - tags: tagAPI, - meta: metaAPI, - users: userAPI, - signUps: signupAPI, - groups: groupAPI, - about: aboutAPI, - shoppingLists: shoppingListsAPI, -}; diff --git a/frontend.old/src/api/mealplan.js b/frontend.old/src/api/mealplan.js deleted file mode 100644 index f1e3dc8d219e..000000000000 --- a/frontend.old/src/api/mealplan.js +++ /dev/null @@ -1,57 +0,0 @@ -import { apiReq } from "./api-utils"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -export const mealplanAPI = { - create(postBody) { - return apiReq.post( - API_ROUTES.mealPlansCreate, - postBody, - () => i18n.t("meal-plan.mealplan-creation-failed"), - () => i18n.t("meal-plan.mealplan-created") - ); - }, - - async all() { - let response = await apiReq.get(API_ROUTES.mealPlansAll); - return response; - }, - - async thisWeek() { - let response = await apiReq.get(API_ROUTES.mealPlansThisWeek); - return response.data; - }, - - async today() { - let response = await apiReq.get(API_ROUTES.mealPlansToday); - return response; - }, - - async getById(id) { - let response = await apiReq.get(API_ROUTES.mealPlansId(id)); - return response.data; - }, - - delete(id) { - return apiReq.delete( - API_ROUTES.mealPlansId(id), - null, - () => i18n.t("meal-plan.mealplan-deletion-failed"), - () => i18n.t("meal-plan.mealplan-deleted") - ); - }, - - update(id, body) { - return apiReq.put( - API_ROUTES.mealPlansId(id), - body, - () => i18n.t("meal-plan.mealplan-update-failed"), - () => i18n.t("meal-plan.mealplan-updated") - ); - }, - - async shoppingList(id) { - let response = await apiReq.get(API_ROUTES.mealPlansIdShoppingList(id)); - return response.data; - }, -}; diff --git a/frontend.old/src/api/meta.js b/frontend.old/src/api/meta.js deleted file mode 100644 index b460c961c17a..000000000000 --- a/frontend.old/src/api/meta.js +++ /dev/null @@ -1,29 +0,0 @@ -import { apiReq } from "./api-utils"; -import { API_ROUTES } from "./apiRoutes"; - -export const metaAPI = { - async getAppInfo() { - const response = await apiReq.get(API_ROUTES.debugVersion); - return response.data; - }, - - async getDebugInfo() { - const response = await apiReq.get(API_ROUTES.debug); - return response.data; - }, - - async getLogText(num) { - const response = await apiReq.get(API_ROUTES.debugLogNum(num)); - return response.data; - }, - - async getLastJson() { - const response = await apiReq.get(API_ROUTES.debugLastRecipeJson); - return response.data; - }, - - async getStatistics() { - const response = await apiReq.get(API_ROUTES.debugStatistics); - return response.data; - }, -}; diff --git a/frontend.old/src/api/migration.js b/frontend.old/src/api/migration.js deleted file mode 100644 index 96d148a88e64..000000000000 --- a/frontend.old/src/api/migration.js +++ /dev/null @@ -1,25 +0,0 @@ -import { apiReq } from "./api-utils"; -import { store } from "../store"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -export const migrationAPI = { - async getMigrations() { - let response = await apiReq.get(API_ROUTES.migrations); - return response.data; - }, - async delete(folder, file) { - const response = await apiReq.delete( - API_ROUTES.migrationsImportTypeFileNameDelete(folder, file), - null, - () => i18n.t("general.file-folder-not-found"), - () => i18n.t("migration.migration-data-removed") - ); - return response; - }, - async import(folder, file) { - let response = await apiReq.post(API_ROUTES.migrationsImportTypeFileNameImport(folder, file)); - store.dispatch("requestRecentRecipes"); - return response.data; - }, -}; diff --git a/frontend.old/src/api/recipe.js b/frontend.old/src/api/recipe.js deleted file mode 100644 index 6a4445c7fd31..000000000000 --- a/frontend.old/src/api/recipe.js +++ /dev/null @@ -1,181 +0,0 @@ -import { API_ROUTES } from "./apiRoutes"; -import { apiReq } from "./api-utils"; -import { store } from "../store"; -import i18n from "@/i18n.js"; - -export const recipeAPI = { - /** - * Returns the Default Recipe Settings for the Site - * @returns {AxoisResponse} Axois Response Object - */ - async getDefaultSettings() { - const response = await apiReq.get(API_ROUTES.aboutRecipesDefaults); - return response; - }, - - /** - * Create a Recipe by URL - * @param {string} recipeURL - * @returns {string} Recipe Slug - */ - async createByURL(recipeURL) { - const response = await apiReq.post(API_ROUTES.recipesCreateUrl, { url: recipeURL }, false, () => - i18n.t("recipe.recipe-created") - ); - - store.dispatch("requestRecentRecipes"); - return response; - }, - - async getAllByCategory(categories) { - let response = await apiReq.post(API_ROUTES.recipesCategory, categories); - return response.data; - }, - - async create(recipeData) { - const response = await apiReq.post( - API_ROUTES.recipesCreate, - recipeData, - () => i18n.t("recipe.recipe-creation-failed"), - () => i18n.t("recipe.recipe-created") - ); - store.dispatch("requestRecentRecipes"); - return response.data; - }, - - async requestDetails(recipeSlug) { - const response = await apiReq.getSafe(API_ROUTES.recipesRecipeSlug(recipeSlug)); - return response; - }, - - updateImage(recipeSlug, fileObject, overrideSuccessMsg = false) { - const formData = new FormData(); - formData.append("image", fileObject); - formData.append("extension", fileObject.name.split(".").pop()); - - let successMessage = null; - if (!overrideSuccessMsg) { - successMessage = function() { - return overrideSuccessMsg ? null : i18n.t("recipe.recipe-image-updated"); - }; - } - - return apiReq.put( - API_ROUTES.recipesRecipeSlugImage(recipeSlug), - formData, - () => i18n.t("general.image-upload-failed"), - successMessage - ); - }, - - async createAsset(recipeSlug, fileObject, name, icon) { - const fd = new FormData(); - fd.append("file", fileObject); - fd.append("extension", fileObject.name.split(".").pop()); - fd.append("name", name); - fd.append("icon", icon); - const response = apiReq.post(API_ROUTES.recipesRecipeSlugAssets(recipeSlug), fd); - return response; - }, - - updateImagebyURL(slug, url) { - return apiReq.post( - API_ROUTES.recipesRecipeSlugImage(slug), - { url: url }, - () => i18n.t("general.image-upload-failed"), - () => i18n.t("recipe.recipe-image-updated") - ); - }, - - async update(data) { - let response = await apiReq.put( - API_ROUTES.recipesRecipeSlug(data.slug), - data, - () => i18n.t("recipe.recipe-update-failed"), - () => i18n.t("recipe.recipe-updated") - ); - if (response) { - store.dispatch("patchRecipe", response.data); - return response.data.slug; // ! Temporary until I rewrite to refresh page without additional request - } - }, - - async patch(data) { - let response = await apiReq.patch(API_ROUTES.recipesRecipeSlug(data.slug), data); - store.dispatch("patchRecipe", response.data); - return response.data; - }, - - async delete(recipeSlug) { - const response = await apiReq.delete( - API_ROUTES.recipesRecipeSlug(recipeSlug), - null, - () => i18n.t("recipe.unable-to-delete-recipe"), - () => i18n.t("recipe.recipe-deleted") - ); - store.dispatch("dropRecipe", response.data); - return response; - }, - - async allSummary(start = 0, limit = 9999) { - const response = await apiReq.get(API_ROUTES.recipesSummary, { - params: { start: start, limit: limit }, - }); - return response.data; - }, - - async allUntagged() { - const response = await apiReq.get(API_ROUTES.recipesSummaryUntagged); - return response.data; - }, - - async allUnategorized() { - const response = await apiReq.get(API_ROUTES.recipesSummaryUncategorized); - return response.data; - }, - - recipeImage(recipeSlug, version = null, key = null) { - return `/api/media/recipes/${recipeSlug}/images/original.webp?&rnd=${key}&version=${version}`; - }, - - recipeSmallImage(recipeSlug, version = null, key = null) { - return `/api/media/recipes/${recipeSlug}/images/min-original.webp?&rnd=${key}&version=${version}`; - }, - - recipeTinyImage(recipeSlug, version = null, key = null) { - return `/api/media/recipes/${recipeSlug}/images/tiny-original.webp?&rnd=${key}&version=${version}`; - }, - - recipeAssetPath(recipeSlug, assetName) { - return `/api/media/recipes/${recipeSlug}/assets/${assetName}`; - }, - - /** Create comment in the Database - * @param slug - */ - async createComment(slug, data) { - const response = await apiReq.post(API_ROUTES.recipesSlugComments(slug), data); - return response.data; - }, - /** Update comment in the Database - * @param slug - * @param id - */ - async updateComment(slug, id, data) { - const response = await apiReq.put(API_ROUTES.recipesSlugCommentsId(slug, id), data); - return response.data; - }, - /** Delete comment from the Database - * @param slug - * @param id - */ - async deleteComment(slug, id) { - const response = await apiReq.delete(API_ROUTES.recipesSlugCommentsId(slug, id)); - return response.data; - }, - - async testScrapeURL(url) { - const response = await apiReq.post(API_ROUTES.recipesTestScrapeUrl, { url: url }); - return response.data; - }, -}; diff --git a/frontend.old/src/api/settings.js b/frontend.old/src/api/settings.js deleted file mode 100644 index ffb2af344bbc..000000000000 --- a/frontend.old/src/api/settings.js +++ /dev/null @@ -1,19 +0,0 @@ -import { apiReq } from "./api-utils"; -import { API_ROUTES } from "./apiRoutes"; - -export const settingsAPI = { - async requestAll() { - let response = await apiReq.get(API_ROUTES.siteSettings); - return response.data; - }, - - async testWebhooks() { - let response = await apiReq.post(API_ROUTES.siteSettingsWebhooksTest); - return response.data; - }, - - async update(body) { - let response = await apiReq.put(API_ROUTES.siteSettings, body); - return response.data; - }, -}; diff --git a/frontend.old/src/api/shoppingLists.js b/frontend.old/src/api/shoppingLists.js deleted file mode 100644 index 74a7c9e2cd1c..000000000000 --- a/frontend.old/src/api/shoppingLists.js +++ /dev/null @@ -1,33 +0,0 @@ -// 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.old/src/api/signUps.js b/frontend.old/src/api/signUps.js deleted file mode 100644 index 946a8ea71063..000000000000 --- a/frontend.old/src/api/signUps.js +++ /dev/null @@ -1,35 +0,0 @@ -import { apiReq } from "./api-utils"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -export const signupAPI = { - async getAll() { - let response = await apiReq.get(API_ROUTES.usersSignUps); - return response.data; - }, - async createToken(data) { - let response = await apiReq.post( - API_ROUTES.usersSignUps, - data, - () => i18n.t("signup.sign-up-link-creation-failed"), - () => i18n.t("signup.sign-up-link-created") - ); - return response.data; - }, - async deleteToken(token) { - return await apiReq.delete( - API_ROUTES.usersSignUpsToken(token), - null, - () => i18n.t("signup.sign-up-token-deletion-failed"), - () => i18n.t("signup.sign-up-token-deleted") - ); - }, - async createUser(token, data) { - return apiReq.post( - API_ROUTES.usersSignUpsToken(token), - data, - () => i18n.t("user.you-are-not-allowed-to-create-a-user"), - () => i18n.t("user.user-created") - ); - }, -}; diff --git a/frontend.old/src/api/siteSettings.js b/frontend.old/src/api/siteSettings.js deleted file mode 100644 index 0d25479df24c..000000000000 --- a/frontend.old/src/api/siteSettings.js +++ /dev/null @@ -1,71 +0,0 @@ -import { apiReq } from "./api-utils"; -import { store } from "@/store"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -export const siteSettingsAPI = { - async get() { - let response = await apiReq.get(API_ROUTES.siteSettings); - return response.data; - }, - - async update(body) { - const response = await apiReq.put( - API_ROUTES.siteSettings, - body, - () => i18n.t("settings.settings-update-failed"), - () => i18n.t("settings.settings-updated") - ); - if (response) { - store.dispatch("requestSiteSettings"); - } - return response; - }, - - async getPages() { - let response = await apiReq.get(API_ROUTES.siteSettingsCustomPages); - return response.data; - }, - - async getPage(id) { - let response = await apiReq.get(API_ROUTES.siteSettingsCustomPagesId(id)); - return response.data; - }, - - createPage(body) { - return apiReq.post( - API_ROUTES.siteSettingsCustomPages, - body, - () => i18n.t("page.page-creation-failed"), - () => i18n.t("page.new-page-created") - ); - }, - - async deletePage(id) { - return await apiReq.delete( - API_ROUTES.siteSettingsCustomPagesId(id), - null, - () => i18n.t("page.page-deletion-failed"), - () => i18n.t("page.page-deleted") - ); - }, - - updatePage(body) { - return apiReq.put( - API_ROUTES.siteSettingsCustomPagesId(body.id), - body, - () => i18n.t("page.page-update-failed"), - () => i18n.t("page.page-updated") - ); - }, - - async updateAllPages(allPages) { - let response = await apiReq.put( - API_ROUTES.siteSettingsCustomPages, - allPages, - () => i18n.t("page.pages-update-failed"), - () => i18n.t("page.pages-updated") - ); - return response; - }, -}; diff --git a/frontend.old/src/api/themes.js b/frontend.old/src/api/themes.js deleted file mode 100644 index ba19e0083c64..000000000000 --- a/frontend.old/src/api/themes.js +++ /dev/null @@ -1,42 +0,0 @@ -import { apiReq } from "./api-utils"; -import i18n from "@/i18n.js"; -import { API_ROUTES } from "./apiRoutes"; - -export const themeAPI = { - async requestAll() { - let response = await apiReq.get(API_ROUTES.themes); - return response.data; - }, - - async requestByName(name) { - let response = await apiReq.get(API_ROUTES.themesId(name)); - return response.data; - }, - - async create(postBody) { - return await apiReq.post( - API_ROUTES.themesCreate, - postBody, - () => i18n.t("settings.theme.error-creating-theme-see-log-file"), - () => i18n.t("settings.theme.theme-saved") - ); - }, - - update(data) { - return apiReq.put( - API_ROUTES.themesId(data.id), - data, - () => i18n.t("settings.theme.error-updating-theme"), - () => i18n.t("settings.theme.theme-updated") - ); - }, - - delete(id) { - return apiReq.delete( - API_ROUTES.themesId(id), - null, - () => i18n.t("settings.theme.error-deleting-theme"), - () => i18n.t("settings.theme.theme-deleted") - ); - }, -}; diff --git a/frontend.old/src/api/upload.js b/frontend.old/src/api/upload.js deleted file mode 100644 index efd9ada3e1fc..000000000000 --- a/frontend.old/src/api/upload.js +++ /dev/null @@ -1,14 +0,0 @@ -import { apiReq } from "./api-utils"; -import i18n from "@/i18n.js"; - -export const utilsAPI = { - // import { api } from "@/api"; - uploadFile(url, fileObject) { - return apiReq.post( - url, - fileObject, - () => i18n.t("general.failure-uploading-file"), - () => i18n.t("general.file-uploaded") - ); - }, -}; diff --git a/frontend.old/src/api/users.js b/frontend.old/src/api/users.js deleted file mode 100644 index d354a6e6614b..000000000000 --- a/frontend.old/src/api/users.js +++ /dev/null @@ -1,107 +0,0 @@ -import { API_ROUTES } from "./apiRoutes"; -import { apiReq } from "./api-utils"; -import i18n from "@/i18n.js"; - -export const userAPI = { - async login(formData) { - let response = await apiReq.post(API_ROUTES.authToken, formData, null, () => { - return i18n.t("user.user-successfully-logged-in"); - }); - return response; - }, - async refresh() { - return apiReq.getSafe(API_ROUTES.authRefresh); - }, - async allUsers() { - let response = await apiReq.get(API_ROUTES.users); - return response.data; - }, - create(user) { - return apiReq.post( - API_ROUTES.users, - user, - () => i18n.t("user.user-creation-failed"), - () => i18n.t("user.user-created") - ); - }, - async self() { - return apiReq.getSafe(API_ROUTES.usersSelf); - }, - async byID(id) { - let response = await apiReq.get(API_ROUTES.usersId(id)); - return response.data; - }, - update(user) { - return apiReq.put( - API_ROUTES.usersId(user.id), - user, - () => i18n.t("user.user-update-failed"), - () => i18n.t("user.user-updated") - ); - }, - changePassword(id, password) { - return apiReq.put( - API_ROUTES.usersIdPassword(id), - password, - () => i18n.t("user.existing-password-does-not-match"), - () => i18n.t("user.password-updated") - ); - }, - - delete(id) { - return apiReq.delete(API_ROUTES.usersId(id), null, deleteErrorText, () => { - return i18n.t("user.user-deleted"); - }); - }, - resetPassword(id) { - return apiReq.put( - API_ROUTES.usersIdResetPassword(id), - null, - () => i18n.t("user.password-reset-failed"), - () => i18n.t("user.password-has-been-reset-to-the-default-password") - ); - }, - async createAPIToken(name) { - const response = await apiReq.post(API_ROUTES.usersApiTokens, { name }); - return response.data; - }, - async deleteAPIToken(id) { - const response = await apiReq.delete(API_ROUTES.usersApiTokensTokenId(id)); - return response.data; - }, - /** Adds a Recipe to the users favorites - * @param id - */ - async getFavorites(id) { - const response = await apiReq.get(API_ROUTES.usersIdFavorites(id)); - return response.data; - }, - /** Adds a Recipe to the users favorites - * @param id - */ - async addFavorite(id, slug) { - const response = await apiReq.post(API_ROUTES.usersIdFavoritesSlug(id, slug)); - return response.data; - }, - /** Adds a Recipe to the users favorites - * @param id - */ - async removeFavorite(id, slug) { - const response = await apiReq.delete(API_ROUTES.usersIdFavoritesSlug(id, slug)); - return response.data; - }, - - userProfileImage(id) { - if (!id || id === undefined) return; - return `/api/users/${id}/image`; - }, -}; - -const deleteErrorText = response => { - switch (response.data.detail) { - case "SUPER_USER": - return i18n.t("user.error-cannot-delete-super-user"); - default: - return i18n.t("user.you-are-not-allowed-to-delete-this-user"); - } -}; diff --git a/frontend.old/src/components/Fallbacks/NoRecipe.vue b/frontend.old/src/components/Fallbacks/NoRecipe.vue deleted file mode 100644 index d196b487c05b..000000000000 --- a/frontend.old/src/components/Fallbacks/NoRecipe.vue +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/frontend.old/src/components/Fallbacks/The404.vue b/frontend.old/src/components/Fallbacks/The404.vue deleted file mode 100644 index 60f8d5ba19cd..000000000000 --- a/frontend.old/src/components/Fallbacks/The404.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/components/FormHelpers/CategoryTagSelector.vue b/frontend.old/src/components/FormHelpers/CategoryTagSelector.vue deleted file mode 100644 index e02b988b48ef..000000000000 --- a/frontend.old/src/components/FormHelpers/CategoryTagSelector.vue +++ /dev/null @@ -1,134 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/FormHelpers/ColorPickerDialog.vue b/frontend.old/src/components/FormHelpers/ColorPickerDialog.vue deleted file mode 100644 index 13ba003ec499..000000000000 --- a/frontend.old/src/components/FormHelpers/ColorPickerDialog.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/FormHelpers/DatePicker.vue b/frontend.old/src/components/FormHelpers/DatePicker.vue deleted file mode 100644 index 610ec00a8970..000000000000 --- a/frontend.old/src/components/FormHelpers/DatePicker.vue +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/FormHelpers/ImportOptions.vue b/frontend.old/src/components/FormHelpers/ImportOptions.vue deleted file mode 100644 index 519037a46e53..000000000000 --- a/frontend.old/src/components/FormHelpers/ImportOptions.vue +++ /dev/null @@ -1,69 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend.old/src/components/FormHelpers/LanguageSelector.vue b/frontend.old/src/components/FormHelpers/LanguageSelector.vue deleted file mode 100644 index c480fad998d5..000000000000 --- a/frontend.old/src/components/FormHelpers/LanguageSelector.vue +++ /dev/null @@ -1,48 +0,0 @@ - - - diff --git a/frontend.old/src/components/FormHelpers/TimePickerDialog.vue b/frontend.old/src/components/FormHelpers/TimePickerDialog.vue deleted file mode 100644 index 8a7c9e851a22..000000000000 --- a/frontend.old/src/components/FormHelpers/TimePickerDialog.vue +++ /dev/null @@ -1,42 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/ImportSummaryDialog/DataTable.vue b/frontend.old/src/components/ImportSummaryDialog/DataTable.vue deleted file mode 100644 index 8391bc95eedf..000000000000 --- a/frontend.old/src/components/ImportSummaryDialog/DataTable.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/ImportSummaryDialog/index.vue b/frontend.old/src/components/ImportSummaryDialog/index.vue deleted file mode 100644 index d754451fd0f7..000000000000 --- a/frontend.old/src/components/ImportSummaryDialog/index.vue +++ /dev/null @@ -1,142 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/Login/LoginDialog.vue b/frontend.old/src/components/Login/LoginDialog.vue deleted file mode 100644 index fae060b1d075..000000000000 --- a/frontend.old/src/components/Login/LoginDialog.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/Login/LoginForm.vue b/frontend.old/src/components/Login/LoginForm.vue deleted file mode 100644 index d3bda238cce9..000000000000 --- a/frontend.old/src/components/Login/LoginForm.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/Login/SignUpForm.vue b/frontend.old/src/components/Login/SignUpForm.vue deleted file mode 100644 index 4aa118f8f30a..000000000000 --- a/frontend.old/src/components/Login/SignUpForm.vue +++ /dev/null @@ -1,141 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/MealPlan/ShoppingListDialog.vue b/frontend.old/src/components/MealPlan/ShoppingListDialog.vue deleted file mode 100644 index d66d27219a89..000000000000 --- a/frontend.old/src/components/MealPlan/ShoppingListDialog.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/Buttons/TheButton.vue b/frontend.old/src/components/UI/Buttons/TheButton.vue deleted file mode 100644 index a3973429b90d..000000000000 --- a/frontend.old/src/components/UI/Buttons/TheButton.vue +++ /dev/null @@ -1,163 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/components/UI/Buttons/TheCopyButton.vue b/frontend.old/src/components/UI/Buttons/TheCopyButton.vue deleted file mode 100644 index adfa5a11ddd2..000000000000 --- a/frontend.old/src/components/UI/Buttons/TheCopyButton.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/components/UI/Buttons/TheDownloadBtn.vue b/frontend.old/src/components/UI/Buttons/TheDownloadBtn.vue deleted file mode 100644 index 02e7a21a30e8..000000000000 --- a/frontend.old/src/components/UI/Buttons/TheDownloadBtn.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/Buttons/TheUploadBtn.vue b/frontend.old/src/components/UI/Buttons/TheUploadBtn.vue deleted file mode 100644 index 95b4a8ce5262..000000000000 --- a/frontend.old/src/components/UI/Buttons/TheUploadBtn.vue +++ /dev/null @@ -1,89 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/Dialogs/BackupDialog.vue b/frontend.old/src/components/UI/Dialogs/BackupDialog.vue deleted file mode 100644 index d9edb45efc53..000000000000 --- a/frontend.old/src/components/UI/Dialogs/BackupDialog.vue +++ /dev/null @@ -1,146 +0,0 @@ - - - diff --git a/frontend.old/src/components/UI/Dialogs/ImportDialog.vue b/frontend.old/src/components/UI/Dialogs/ImportDialog.vue deleted file mode 100644 index af7002f4cd3b..000000000000 --- a/frontend.old/src/components/UI/Dialogs/ImportDialog.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/Dialogs/SearchDialog.vue b/frontend.old/src/components/UI/Dialogs/SearchDialog.vue deleted file mode 100644 index c45dbf9ae276..000000000000 --- a/frontend.old/src/components/UI/Dialogs/SearchDialog.vue +++ /dev/null @@ -1,172 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/components/UI/GlobalSnackbar.vue b/frontend.old/src/components/UI/GlobalSnackbar.vue deleted file mode 100644 index 39b9cfab127c..000000000000 --- a/frontend.old/src/components/UI/GlobalSnackbar.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend.old/src/components/UI/LogCard.vue b/frontend.old/src/components/UI/LogCard.vue deleted file mode 100644 index 9fbc9a906cda..000000000000 --- a/frontend.old/src/components/UI/LogCard.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/components/UI/Search/FuseSearchBar.vue b/frontend.old/src/components/UI/Search/FuseSearchBar.vue deleted file mode 100644 index fa109197e945..000000000000 --- a/frontend.old/src/components/UI/Search/FuseSearchBar.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/Search/SearchBar.vue b/frontend.old/src/components/UI/Search/SearchBar.vue deleted file mode 100644 index bf2b145aec01..000000000000 --- a/frontend.old/src/components/UI/Search/SearchBar.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - - diff --git a/frontend.old/src/components/UI/Search/SearchDialog.vue b/frontend.old/src/components/UI/Search/SearchDialog.vue deleted file mode 100644 index ea4a6f1918f8..000000000000 --- a/frontend.old/src/components/UI/Search/SearchDialog.vue +++ /dev/null @@ -1,103 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/StatCard.vue b/frontend.old/src/components/UI/StatCard.vue deleted file mode 100644 index a9f11c9451f3..000000000000 --- a/frontend.old/src/components/UI/StatCard.vue +++ /dev/null @@ -1,103 +0,0 @@ -w - - - - diff --git a/frontend.old/src/components/UI/TheAppBar.vue b/frontend.old/src/components/UI/TheAppBar.vue deleted file mode 100644 index e54393d7301d..000000000000 --- a/frontend.old/src/components/UI/TheAppBar.vue +++ /dev/null @@ -1,102 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/TheRecipeFab.vue b/frontend.old/src/components/UI/TheRecipeFab.vue deleted file mode 100644 index 8136527ff534..000000000000 --- a/frontend.old/src/components/UI/TheRecipeFab.vue +++ /dev/null @@ -1,247 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/TheSidebar.vue b/frontend.old/src/components/UI/TheSidebar.vue deleted file mode 100644 index 5d64acde2db6..000000000000 --- a/frontend.old/src/components/UI/TheSidebar.vue +++ /dev/null @@ -1,253 +0,0 @@ - - - - - diff --git a/frontend.old/src/components/UI/TheSiteMenu.vue b/frontend.old/src/components/UI/TheSiteMenu.vue deleted file mode 100644 index b9de04a1086b..000000000000 --- a/frontend.old/src/components/UI/TheSiteMenu.vue +++ /dev/null @@ -1,109 +0,0 @@ - - - - diff --git a/frontend.old/src/i18n.js b/frontend.old/src/i18n.js deleted file mode 100644 index 7433c36a8b24..000000000000 --- a/frontend.old/src/i18n.js +++ /dev/null @@ -1,38 +0,0 @@ -import Vue from "vue"; -import VueI18n from "vue-i18n"; -import Vuetify from "@/plugins/vuetify"; -import axios from 'axios'; - -Vue.use(VueI18n); - -const i18n = new VueI18n(); - -export default i18n; - -const loadedLanguages = []; - -function setI18nLanguage (lang) { - i18n.locale = lang; - Vuetify.framework.lang.current = lang; - axios.defaults.headers.common['Accept-Language'] = lang - document.querySelector('html').setAttribute('lang', lang) - return lang -} - -export function loadLanguageAsync(lang) { - - if ( ! loadedLanguages.includes(lang)) { - const messages = import(`./locales/messages/${lang}.json`); - const dateTimeFormats = import(`./locales/dateTimeFormats/${lang}.json`); - - return Promise.all([messages, dateTimeFormats]).then( - values => { - i18n.setLocaleMessage(lang, values[0].default) - i18n.setDateTimeFormat(lang, values[1].default) - loadedLanguages.push(lang) - return setI18nLanguage(lang) - } - ) - } - return Promise.resolve(setI18nLanguage(lang)) -} \ No newline at end of file diff --git a/frontend.old/src/installCompAPI.js b/frontend.old/src/installCompAPI.js deleted file mode 100644 index c1a45139dba5..000000000000 --- a/frontend.old/src/installCompAPI.js +++ /dev/null @@ -1,3 +0,0 @@ -import Vue from "vue"; -import VueCompositionAPI from "@vue/composition-api"; -Vue.use(VueCompositionAPI); diff --git a/frontend.old/src/main.js b/frontend.old/src/main.js deleted file mode 100644 index d7ad28f64372..000000000000 --- a/frontend.old/src/main.js +++ /dev/null @@ -1,44 +0,0 @@ -import "./installCompAPI"; // Must Be First - -import Vue from "vue"; -import App from "./App.vue"; -import vuetify from "./plugins/vuetify"; -import store from "./store"; -import VueRouter from "vue-router"; -import { router } from "./routes"; -import { globals } from "@/utils/globals"; -import i18n from "./i18n"; -import "typeface-roboto/index.css"; -import "./registerServiceWorker"; - -Vue.config.productionTip = false; -Vue.use(VueRouter); -Vue.component("TheButton", () => import("@/components/UI/Buttons/TheButton.vue")); - -Vue.prototype.$globals = globals; - -const vueApp = new Vue({ - vuetify, - store, - router, - i18n, - render: h => h(App), -}).$mount("#app"); - -// Truncate -const truncate = function(text, length, clamp) { - clamp = clamp || "..."; - let node = document.createElement("div"); - node.innerHTML = text; - let content = node.textContent; - return content.length > length ? content.slice(0, length) + clamp : content; -}; - -const titleCase = function(value) { - return value.replace(/(?:^|\s|-)\S/g, x => x.toUpperCase()); -}; - -Vue.filter("truncate", truncate); -Vue.filter("titleCase", titleCase); - -export { router, vueApp }; diff --git a/frontend.old/src/mixins/initials.js b/frontend.old/src/mixins/initials.js deleted file mode 100644 index 60448d1be521..000000000000 --- a/frontend.old/src/mixins/initials.js +++ /dev/null @@ -1,18 +0,0 @@ -export const initials = { - computed: { - initials() { - if (!this.user.fullName) return "00" - const allNames = this.user.fullName.trim().split(" "); - const initials = allNames.reduce( - (acc, curr, index) => { - if (index === 0 || index === allNames.length - 1) { - acc = `${acc}${curr.charAt(0).toUpperCase()}`; - } - return acc; - }, - [""] - ); - return initials; - }, - }, -}; diff --git a/frontend.old/src/mixins/user.js b/frontend.old/src/mixins/user.js deleted file mode 100644 index 2d500bea42d9..000000000000 --- a/frontend.old/src/mixins/user.js +++ /dev/null @@ -1,24 +0,0 @@ -import { store } from "@/store"; -export const user = { - computed: { - user() { - return store.getters.getUserData; - }, - loggedIn() { - return store.getters.getIsLoggedIn; - }, - initials() { - const allNames = this.user.fullName.trim().split(" "); - const initials = allNames.reduce( - (acc, curr, index) => { - if (index === 0 || index === allNames.length - 1) { - acc = `${acc}${curr.charAt(0).toUpperCase()}`; - } - return acc; - }, - [""] - ); - return initials; - }, - }, -}; diff --git a/frontend.old/src/mixins/utilMixins.js b/frontend.old/src/mixins/utilMixins.js deleted file mode 100644 index 2b9bf8504264..000000000000 --- a/frontend.old/src/mixins/utilMixins.js +++ /dev/null @@ -1,7 +0,0 @@ -export const utilMixins = { - commputed: { - isMobile() { - return this.$vuetify.breakpoint.name === "xs"; - }, - }, -}; diff --git a/frontend.old/src/mixins/validators.js b/frontend.old/src/mixins/validators.js deleted file mode 100644 index ba795b3effc8..000000000000 --- a/frontend.old/src/mixins/validators.js +++ /dev/null @@ -1,15 +0,0 @@ -const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@(([[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - -export const validators = { - data() { - return { - emailRule: v => !v || EMAIL_REGEX.test(v) || this.$t("user.e-mail-must-be-valid"), - - existsRule: value => !!value || this.$t("general.field-required"), - - minRule: v => v.length >= 8 || this.$t("user.use-8-characters-or-more-for-your-password"), - - whiteSpace: v => !v || v.split(" ").length <= 1 || this.$t("recipe.no-white-space-allowed"), - }; - }, -}; diff --git a/frontend.old/src/pages/404Page.vue b/frontend.old/src/pages/404Page.vue deleted file mode 100644 index e98fab51ceee..000000000000 --- a/frontend.old/src/pages/404Page.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/frontend.old/src/pages/Admin/About/index.vue b/frontend.old/src/pages/Admin/About/index.vue deleted file mode 100644 index fae2ee76d1db..000000000000 --- a/frontend.old/src/pages/Admin/About/index.vue +++ /dev/null @@ -1,130 +0,0 @@ - - - diff --git a/frontend.old/src/pages/Admin/Dashboard/BackupViewer.vue b/frontend.old/src/pages/Admin/Dashboard/BackupViewer.vue deleted file mode 100644 index e65d29622457..000000000000 --- a/frontend.old/src/pages/Admin/Dashboard/BackupViewer.vue +++ /dev/null @@ -1,162 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/pages/Admin/Dashboard/EventViewer.vue b/frontend.old/src/pages/Admin/Dashboard/EventViewer.vue deleted file mode 100644 index 5dcd44ec397b..000000000000 --- a/frontend.old/src/pages/Admin/Dashboard/EventViewer.vue +++ /dev/null @@ -1,135 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/pages/Admin/Dashboard/index.vue b/frontend.old/src/pages/Admin/Dashboard/index.vue deleted file mode 100644 index f217e700551c..000000000000 --- a/frontend.old/src/pages/Admin/Dashboard/index.vue +++ /dev/null @@ -1,126 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/pages/Admin/ManageUsers/GroupCard.vue b/frontend.old/src/pages/Admin/ManageUsers/GroupCard.vue deleted file mode 100644 index 3ab26d3f2860..000000000000 --- a/frontend.old/src/pages/Admin/ManageUsers/GroupCard.vue +++ /dev/null @@ -1,122 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ManageUsers/GroupDashboard.vue b/frontend.old/src/pages/Admin/ManageUsers/GroupDashboard.vue deleted file mode 100644 index 1b83f80100b1..000000000000 --- a/frontend.old/src/pages/Admin/ManageUsers/GroupDashboard.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ManageUsers/TheSignUpTable.vue b/frontend.old/src/pages/Admin/ManageUsers/TheSignUpTable.vue deleted file mode 100644 index 95a442535eb4..000000000000 --- a/frontend.old/src/pages/Admin/ManageUsers/TheSignUpTable.vue +++ /dev/null @@ -1,230 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ManageUsers/TheUserTable.vue b/frontend.old/src/pages/Admin/ManageUsers/TheUserTable.vue deleted file mode 100644 index 45f0350057ed..000000000000 --- a/frontend.old/src/pages/Admin/ManageUsers/TheUserTable.vue +++ /dev/null @@ -1,284 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ManageUsers/index.vue b/frontend.old/src/pages/Admin/ManageUsers/index.vue deleted file mode 100644 index 15e1888877ef..000000000000 --- a/frontend.old/src/pages/Admin/ManageUsers/index.vue +++ /dev/null @@ -1,68 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Migration/MigrationCard.vue b/frontend.old/src/pages/Admin/Migration/MigrationCard.vue deleted file mode 100644 index 54492f0486eb..000000000000 --- a/frontend.old/src/pages/Admin/Migration/MigrationCard.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Migration/MigrationDialog.vue b/frontend.old/src/pages/Admin/Migration/MigrationDialog.vue deleted file mode 100644 index 5b5b3ab7bf54..000000000000 --- a/frontend.old/src/pages/Admin/Migration/MigrationDialog.vue +++ /dev/null @@ -1,106 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Migration/index.vue b/frontend.old/src/pages/Admin/Migration/index.vue deleted file mode 100644 index c345bde2996f..000000000000 --- a/frontend.old/src/pages/Admin/Migration/index.vue +++ /dev/null @@ -1,82 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Profile/APITokenCard.vue b/frontend.old/src/pages/Admin/Profile/APITokenCard.vue deleted file mode 100644 index 6977229a5fc7..000000000000 --- a/frontend.old/src/pages/Admin/Profile/APITokenCard.vue +++ /dev/null @@ -1,150 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Profile/ProfileGroupCard.vue b/frontend.old/src/pages/Admin/Profile/ProfileGroupCard.vue deleted file mode 100644 index 27c4f5fb8187..000000000000 --- a/frontend.old/src/pages/Admin/Profile/ProfileGroupCard.vue +++ /dev/null @@ -1,211 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Profile/ProfileThemeCard.vue b/frontend.old/src/pages/Admin/Profile/ProfileThemeCard.vue deleted file mode 100644 index 6a2559eead38..000000000000 --- a/frontend.old/src/pages/Admin/Profile/ProfileThemeCard.vue +++ /dev/null @@ -1,225 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/pages/Admin/Profile/UserCard.vue b/frontend.old/src/pages/Admin/Profile/UserCard.vue deleted file mode 100644 index 11519ce55d35..000000000000 --- a/frontend.old/src/pages/Admin/Profile/UserCard.vue +++ /dev/null @@ -1,198 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Profile/index.vue b/frontend.old/src/pages/Admin/Profile/index.vue deleted file mode 100644 index e6cfa1a38301..000000000000 --- a/frontend.old/src/pages/Admin/Profile/index.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Settings/CreatePageDialog.vue b/frontend.old/src/pages/Admin/Settings/CreatePageDialog.vue deleted file mode 100644 index 355e5a060bce..000000000000 --- a/frontend.old/src/pages/Admin/Settings/CreatePageDialog.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Settings/CustomPageCreator.vue b/frontend.old/src/pages/Admin/Settings/CustomPageCreator.vue deleted file mode 100644 index 2da1ce91f97e..000000000000 --- a/frontend.old/src/pages/Admin/Settings/CustomPageCreator.vue +++ /dev/null @@ -1,126 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Settings/HomePageSettings.vue b/frontend.old/src/pages/Admin/Settings/HomePageSettings.vue deleted file mode 100644 index 14c0c8ec8966..000000000000 --- a/frontend.old/src/pages/Admin/Settings/HomePageSettings.vue +++ /dev/null @@ -1,220 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/Settings/index.vue b/frontend.old/src/pages/Admin/Settings/index.vue deleted file mode 100644 index c94954efcdbd..000000000000 --- a/frontend.old/src/pages/Admin/Settings/index.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/BulkAssign.vue b/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/BulkAssign.vue deleted file mode 100644 index 19c0aec4cfec..000000000000 --- a/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/BulkAssign.vue +++ /dev/null @@ -1,149 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/RemoveUnused.vue b/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/RemoveUnused.vue deleted file mode 100644 index c5df2b71a451..000000000000 --- a/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/RemoveUnused.vue +++ /dev/null @@ -1,89 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue b/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue deleted file mode 100644 index d3fe0715b182..000000000000 --- a/frontend.old/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue +++ /dev/null @@ -1,237 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ToolBox/EventNotification.vue b/frontend.old/src/pages/Admin/ToolBox/EventNotification.vue deleted file mode 100644 index c022dbb3c1e5..000000000000 --- a/frontend.old/src/pages/Admin/ToolBox/EventNotification.vue +++ /dev/null @@ -1,213 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/ToolBox/RecipeOrganizer.vue b/frontend.old/src/pages/Admin/ToolBox/RecipeOrganizer.vue deleted file mode 100644 index 9e08cfb536be..000000000000 --- a/frontend.old/src/pages/Admin/ToolBox/RecipeOrganizer.vue +++ /dev/null @@ -1,91 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/pages/Admin/ToolBox/index.vue b/frontend.old/src/pages/Admin/ToolBox/index.vue deleted file mode 100644 index c80cce98db44..000000000000 --- a/frontend.old/src/pages/Admin/ToolBox/index.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Admin/index.vue b/frontend.old/src/pages/Admin/index.vue deleted file mode 100644 index 55456e5b9030..000000000000 --- a/frontend.old/src/pages/Admin/index.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Debug.vue b/frontend.old/src/pages/Debug.vue deleted file mode 100644 index d4b8db4ee2a2..000000000000 --- a/frontend.old/src/pages/Debug.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/HomePage.vue b/frontend.old/src/pages/HomePage.vue deleted file mode 100644 index 74b8449e824d..000000000000 --- a/frontend.old/src/pages/HomePage.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/LoginPage.vue b/frontend.old/src/pages/LoginPage.vue deleted file mode 100644 index 996b40b5235c..000000000000 --- a/frontend.old/src/pages/LoginPage.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/MealPlan/Planner.vue b/frontend.old/src/pages/MealPlan/Planner.vue deleted file mode 100644 index 15c1dc59c0b7..000000000000 --- a/frontend.old/src/pages/MealPlan/Planner.vue +++ /dev/null @@ -1,150 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/MealPlan/ThisWeek.vue b/frontend.old/src/pages/MealPlan/ThisWeek.vue deleted file mode 100644 index 002d4da30654..000000000000 --- a/frontend.old/src/pages/MealPlan/ThisWeek.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Recipe/NewRecipe.vue b/frontend.old/src/pages/Recipe/NewRecipe.vue deleted file mode 100644 index 6e100865d900..000000000000 --- a/frontend.old/src/pages/Recipe/NewRecipe.vue +++ /dev/null @@ -1,126 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Recipe/ScraperDebugger.vue b/frontend.old/src/pages/Recipe/ScraperDebugger.vue deleted file mode 100644 index 8b91bfb85b0c..000000000000 --- a/frontend.old/src/pages/Recipe/ScraperDebugger.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - diff --git a/frontend.old/src/pages/Recipe/ViewRecipe.vue b/frontend.old/src/pages/Recipe/ViewRecipe.vue deleted file mode 100644 index 3cc353258b9d..000000000000 --- a/frontend.old/src/pages/Recipe/ViewRecipe.vue +++ /dev/null @@ -1,271 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Recipes/AllRecipes.vue b/frontend.old/src/pages/Recipes/AllRecipes.vue deleted file mode 100644 index da1069b18d02..000000000000 --- a/frontend.old/src/pages/Recipes/AllRecipes.vue +++ /dev/null @@ -1,58 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Recipes/CategoryTagPage.vue b/frontend.old/src/pages/Recipes/CategoryTagPage.vue deleted file mode 100644 index eff8a13ddfd6..000000000000 --- a/frontend.old/src/pages/Recipes/CategoryTagPage.vue +++ /dev/null @@ -1,108 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/Recipes/CustomPage.vue b/frontend.old/src/pages/Recipes/CustomPage.vue deleted file mode 100644 index c9b91b70e5ab..000000000000 --- a/frontend.old/src/pages/Recipes/CustomPage.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - diff --git a/frontend.old/src/pages/Recipes/Favorites.vue b/frontend.old/src/pages/Recipes/Favorites.vue deleted file mode 100644 index 950210eff97f..000000000000 --- a/frontend.old/src/pages/Recipes/Favorites.vue +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - diff --git a/frontend.old/src/pages/SearchPage/FilterSelector.vue b/frontend.old/src/pages/SearchPage/FilterSelector.vue deleted file mode 100644 index 43ed22f47b33..000000000000 --- a/frontend.old/src/pages/SearchPage/FilterSelector.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/SearchPage/index.vue b/frontend.old/src/pages/SearchPage/index.vue deleted file mode 100644 index 8d8ffdaa6e4b..000000000000 --- a/frontend.old/src/pages/SearchPage/index.vue +++ /dev/null @@ -1,168 +0,0 @@ - - - - - diff --git a/frontend.old/src/pages/ShoppingList/index.vue b/frontend.old/src/pages/ShoppingList/index.vue deleted file mode 100644 index 051c9eb54c53..000000000000 --- a/frontend.old/src/pages/ShoppingList/index.vue +++ /dev/null @@ -1,281 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend.old/src/pages/SignUpPage.vue b/frontend.old/src/pages/SignUpPage.vue deleted file mode 100644 index d1e79154f9df..000000000000 --- a/frontend.old/src/pages/SignUpPage.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - diff --git a/frontend.old/src/plugins/vuetify.js b/frontend.old/src/plugins/vuetify.js deleted file mode 100644 index 6f54f8ad73ee..000000000000 --- a/frontend.old/src/plugins/vuetify.js +++ /dev/null @@ -1,63 +0,0 @@ -import Vue from "vue"; -import Vuetify from "vuetify/lib"; - -Vue.use(Vuetify); - -import de from "vuetify/es5/locale/de"; -import en from "vuetify/es5/locale/en"; -import es from "vuetify/es5/locale/es"; -import fr from "vuetify/es5/locale/fr"; -import it from "vuetify/es5/locale/it"; -import nl from "vuetify/es5/locale/nl"; -import pl from "vuetify/es5/locale/pl"; -import sv from "vuetify/es5/locale/sv"; -import zhHans from "vuetify/es5/locale/zh-Hans"; - -const vuetify = new Vuetify({ - theme: { - dark: false, - options: { customProperties: true }, - - themes: { - light: { - primary: "#E58325", - accent: "#00457A", - secondary: "#973542", - success: "#43A047", - info: "#FFFD99", - warning: "#FF4081", - error: "#EF5350", - }, - dark: { - primary: "#4527A0", - accent: "#FF4081", - secondary: "#26C6DA", - success: "#43A047", - info: "#2196F3", - warning: "#FB8C00", - error: "#FF5252", - }, - }, - }, - lang: { - locales: { - "de-DE": de, - "en-US": en, - "en-GB": en, - "es-ES": es, - "fr-FR": fr, - "it-IT": it, - "nl-NL": nl, - "pl-PL": pl, - "sv-SE": sv, - "zh-CN": zhHans, - }, - current: "en-US", - }, - icons: { - iconfont: "mdiSvg", - }, -}); - -export default vuetify; -export { vuetify }; diff --git a/frontend.old/src/registerServiceWorker.js b/frontend.old/src/registerServiceWorker.js deleted file mode 100644 index 6cb38651365d..000000000000 --- a/frontend.old/src/registerServiceWorker.js +++ /dev/null @@ -1,39 +0,0 @@ -/* eslint-disable no-console */ - -import { register } from "register-service-worker"; - -if (process.env.NODE_ENV === "production") { - register(`${process.env.BASE_URL}service-worker.js`, { - ready() { - console.log("Service worker is active."); - }, - registered(registration) { - console.log("Service worker has been registered."); - - // Routinely check for app updates by testing for a new service worker. - setInterval(() => { - registration.update(); - }, 1000 * 60 * 60); // hourly checks - }, - cached() { - console.log("Content has been cached for offline use."); - }, - updatefound() { - console.log("New content is downloading."); - }, - updated(registration) { - console.log("New content is available; please refresh."); - - // Add a custom event and dispatch it. - // Used to display of a 'refresh' banner following a service worker update. - // Set the event payload to the service worker registration object. - document.dispatchEvent(new CustomEvent("swUpdated", { detail: registration })); - }, - offline() { - console.log("No internet connection found. App is running in offline mode."); - }, - error(error) { - console.error("Error during service worker registration:", error); - }, - }); -} diff --git a/frontend.old/src/routes/admin.js b/frontend.old/src/routes/admin.js deleted file mode 100644 index 4f580245d0ed..000000000000 --- a/frontend.old/src/routes/admin.js +++ /dev/null @@ -1,74 +0,0 @@ -const Admin = () => import(/* webpackChunkName: "admin-pages" */ "@/pages/Admin"); -const Migration = () => import(/* webpackChunkName: "admin-pages" */ "@/pages/Admin/Migration"); -const Profile = () => import(/* webpackChunkName: "admin-pages" */ "@/pages/Admin/Profile"); -const ManageUsers = () => import(/* webpackChunkName: "admin-pages" */ "@/pages/Admin/ManageUsers"); -const Settings = () => import(/* webpackChunkName: "admin-pages" */ "@/pages/Admin/Settings"); -const About = () => import(/* webpackChunkName: "admin-pages" */ "@/pages/Admin/About"); -const ToolBox = () => import(/* webpackChunkName: "admin-pages" */ "@/pages/Admin/ToolBox"); -const Dashboard = () => import(/* webpackChunkName: "admin-pages" */ "@/pages/Admin/Dashboard"); -import { store } from "../store"; - -export const adminRoutes = { - path: "/admin", - component: Admin, - beforeEnter: (to, _from, next) => { - if (store.getters.getIsLoggedIn) { - next(); - } else next({ path: "/login", query: { redirect: to.fullPath } }); - }, - children: [ - { - path: "", - component: Profile, - }, - { - path: "profile", - component: Profile, - meta: { - title: "settings.profile", - }, - }, - { - path: "migrations", - component: Migration, - meta: { - title: "settings.migrations", - }, - }, - { - path: "manage-users", - component: ManageUsers, - meta: { - title: "user.manage-users", - }, - }, - { - path: "settings", - component: Settings, - meta: { - title: "settings.site-settings", - }, - }, - { - path: "toolbox", - component: ToolBox, - meta: { - title: "settings.toolbox.toolbox", - }, - }, - { - path: "about", - component: About, - meta: { - title: "about.about", - }, - }, - { - path: "dashboard", - component: Dashboard, - meta: { - title: "general.dashboard", - }, - }, - ], -}; diff --git a/frontend.old/src/routes/auth.js b/frontend.old/src/routes/auth.js deleted file mode 100644 index 742472dd31a5..000000000000 --- a/frontend.old/src/routes/auth.js +++ /dev/null @@ -1,18 +0,0 @@ -const LoginPage = () => import("@/pages/LoginPage"); -const SignUpPage = () => import("@/pages/SignUpPage"); -import { store } from "../store"; - -export const authRoutes = [ - { - path: "/logout", - beforeEnter: (_to, _from, next) => { - store.commit("setToken", ""); - store.commit("setIsLoggedIn", false); - next("/"); - }, - }, - { path: "/login", component: LoginPage }, - - { path: "/sign-up", redirect: "/" }, - { path: "/sign-up/:token", component: SignUpPage }, -]; diff --git a/frontend.old/src/routes/general.js b/frontend.old/src/routes/general.js deleted file mode 100644 index f7802fa94718..000000000000 --- a/frontend.old/src/routes/general.js +++ /dev/null @@ -1,16 +0,0 @@ -const SearchPage = () => import("@/pages/SearchPage"); -const ShoppingList = () => import("@/pages/ShoppingList"); -import HomePage from "@/pages/HomePage"; - -export const generalRoutes = [ - { path: "/", name: "home", component: HomePage }, - { path: "/mealie", component: HomePage }, - { path: "/shopping-list", component: ShoppingList }, - { - path: "/search", - component: SearchPage, - meta: { - title: "search.search", - }, - }, -]; diff --git a/frontend.old/src/routes/index.js b/frontend.old/src/routes/index.js deleted file mode 100644 index 9ac473e420b3..000000000000 --- a/frontend.old/src/routes/index.js +++ /dev/null @@ -1,51 +0,0 @@ -import Page404 from "@/pages/404Page"; -import { adminRoutes } from "./admin"; -import { authRoutes } from "./auth"; -import { recipeRoutes } from "./recipes"; -import { mealRoutes } from "./meal"; -import { generalRoutes } from "./general"; -import { store } from "@/store"; -import VueRouter from "vue-router"; -import { loadLanguageAsync } from "@/i18n"; -import Vue from "vue"; -import i18n from "@/i18n.js"; - -export const routes = [ - ...generalRoutes, - adminRoutes, - ...authRoutes, - ...mealRoutes, - ...recipeRoutes, - - { path: "/page-not-found", component: Page404 }, - { path: "*", component: Page404 }, -]; - -const router = new VueRouter({ - base: process.env.BASE_URL, - routes, - mode: process.env.NODE_ENV === "production" ? "history" : "hash", - scrollBehavior() { - return { x: 0, y: 0 }; - }, -}); - -const DEFAULT_TITLE = "Mealie"; -const TITLE_SEPARATOR = "|"; -const TITLE_SUFFIX = " " + TITLE_SEPARATOR + " " + DEFAULT_TITLE; -router.afterEach(to => { - Vue.nextTick(async () => { - if (typeof to.meta.title === "function") { - const title = await to.meta.title(to); - document.title = title + TITLE_SUFFIX; - } else { - document.title = i18n.t(to.meta.title) ? i18n.t(to.meta.title) + TITLE_SUFFIX : DEFAULT_TITLE; - } - }); -}); - -router.beforeEach((__, _, next) => { - loadLanguageAsync(store.getters.getActiveLang).then(() => next()); -}); - -export { router }; diff --git a/frontend.old/src/routes/meal.js b/frontend.old/src/routes/meal.js deleted file mode 100644 index 4aca00dd883e..000000000000 --- a/frontend.old/src/routes/meal.js +++ /dev/null @@ -1,51 +0,0 @@ -const Planner = () => import("@/pages/MealPlan/Planner"); -const ThisWeek = () => import("@/pages/MealPlan/ThisWeek"); -import { api } from "@/api"; -import { utils } from "@/utils"; -import i18n from "@/i18n.js"; - -export const mealRoutes = [ - { - path: "/meal-plan", - component: ThisWeek, - meta: { - title: "meal-plan.dinner-this-week", - }, - }, - { - path: "/meal-plan/planner", - component: Planner, - meta: { - title: "meal-plan.meal-planner", - }, - }, - { - path: "/meal-plan/this-week", - component: ThisWeek, - meta: { - title: "meal-plan.dinner-this-week", - }, - }, - { - path: "/meal-plan/today", - beforeEnter: async (_to, _from, next) => { - await todaysMealRoute().then(redirect => { - if (redirect) { - next(redirect); - } else { - utils.notify.error(i18n.t("meal-plan.no-meal-planned-for-today")); - next(_from); - } - }); - }, - }, -]; - -async function todaysMealRoute() { - const response = await api.mealPlans.today(); - if (response.status == 200 && response.data) { - return "/recipe/" + response.data.slug; - } else { - return null; - } -} diff --git a/frontend.old/src/routes/recipes.js b/frontend.old/src/routes/recipes.js deleted file mode 100644 index e2e3967aac2a..000000000000 --- a/frontend.old/src/routes/recipes.js +++ /dev/null @@ -1,37 +0,0 @@ -const ViewRecipe = () => import(/* webpackChunkName: "recipe-page" */ "@/pages/Recipe/ViewRecipe"); -const NewRecipe = () => import(/* webpackChunkName: "recipe-page" */ "@/pages/Recipe/NewRecipe"); -const ScraperDebugger = () => import("@/pages/Recipe/ScraperDebugger"); -const CustomPage = () => import("@/pages/Recipes/CustomPage"); -const AllRecipes = () => import("@/pages/Recipes/AllRecipes"); -const CategoryTagPage = () => import("@/pages/Recipes/CategoryTagPage"); -const Favorites = () => import("@/pages/Recipes/Favorites"); -import { api } from "@/api"; - -export const recipeRoutes = [ - // Recipes - { path: "/recipes/all", component: AllRecipes }, - { path: "/recipes/debugger", component: ScraperDebugger }, - { path: "/user/:id/favorites", component: Favorites }, - { path: "/recipes/tag/:tag", component: CategoryTagPage }, - { path: "/recipes/tag", component: CategoryTagPage }, - { path: "/recipes/category", component: CategoryTagPage }, - { path: "/recipes/category/:category", component: CategoryTagPage }, - // Misc - { path: "/new/", component: NewRecipe }, - { path: "/pages/:customPage", component: CustomPage }, - - // Recipe Page - { - path: "/recipe/:recipe", - component: ViewRecipe, - meta: { - title: async route => { - const [response, error] = await api.recipes.requestDetails(route.params.recipe); - if (error) console.log({ error }); - const recipe = response.data; - if (recipe && recipe.name) return recipe.name; - else return null; - }, - }, - }, -]; diff --git a/frontend.old/src/store/index.js b/frontend.old/src/store/index.js deleted file mode 100644 index 4ce1d97653ae..000000000000 --- a/frontend.old/src/store/index.js +++ /dev/null @@ -1,80 +0,0 @@ -import Vue from "vue"; -import Vuex from "vuex"; -import { api } from "@/api"; -import createPersistedState from "vuex-persistedstate"; -import userSettings from "./modules/userSettings"; -import language from "./modules/language"; -import siteSettings from "./modules/siteSettings"; -import recipes from "./modules/recipes"; -import groups from "./modules/groups"; -import snackbar from "./modules/snackbar"; - -Vue.use(Vuex); - -const store = new Vuex.Store({ - plugins: [ - createPersistedState({ - paths: ["userSettings", "siteSettings"], - }), - ], - modules: { - userSettings, - language, - siteSettings, - groups, - recipes, - snackbar, - }, - state: { - // All Recipe Data Store - recentRecipes: [], - allRecipes: [], - mealPlanCategories: [], - allCategories: [], - allTags: [], - appInfo: { - version: "", - demoStatus: false, - }, - }, - - mutations: { - setMealPlanCategories(state, payload) { - state.mealPlanCategories = payload; - }, - setAllCategories(state, payload) { - state.allCategories = payload; - }, - setAllTags(state, payload) { - state.allTags = payload; - }, - setAppInfo(state, payload) { - state.appInfo = payload; - }, - }, - - actions: { - async requestCategories({ commit }) { - const categories = await api.categories.getAll(); - commit("setAllCategories", categories); - }, - async requestTags({ commit }) { - const tags = await api.tags.getAll(); - commit("setAllTags", tags); - }, - async requestAppInfo({ commit }) { - const response = await api.meta.getAppInfo(); - commit("setAppInfo", response); - }, - }, - - getters: { - getMealPlanCategories: state => state.mealPlanCategories, - getAllCategories: state => state.allCategories.sort((a, b) => (a.slug > b.slug ? 1 : -1)), - getAllTags: state => state.allTags.sort((a, b) => (a.slug > b.slug ? 1 : -1)), - getAppInfo: state => state.appInfo, - }, -}); - -export default store; -export { store }; diff --git a/frontend.old/src/store/modules/groups.js b/frontend.old/src/store/modules/groups.js deleted file mode 100644 index 03e66fc80dc9..000000000000 --- a/frontend.old/src/store/modules/groups.js +++ /dev/null @@ -1,39 +0,0 @@ -import { api } from "@/api"; - -const state = { - groups: [], - currentGroup: {}, -}; - -const mutations = { - setGroups(state, payload) { - state.groups = payload; - }, - setCurrentGroup(state, payload) { - state.currentGroup = payload; - }, -}; - -const actions = { - async requestAllGroups({ commit }) { - const groups = await api.groups.allGroups(); - commit("setGroups", groups); - }, - async requestCurrentGroup({ commit }) { - const group = await api.groups.current(); - commit("setCurrentGroup", group); - }, -}; - -const getters = { - getGroups: state => state.groups, - getGroupNames: state => Array.from(state.groups, x => x.name), - getCurrentGroup: state => state.currentGroup, -}; - -export default { - state, - mutations, - actions, - getters, -}; diff --git a/frontend.old/src/store/modules/homePage.js b/frontend.old/src/store/modules/homePage.js deleted file mode 100644 index c7b2112b49a4..000000000000 --- a/frontend.old/src/store/modules/homePage.js +++ /dev/null @@ -1,44 +0,0 @@ -import { api } from "@/api"; - -const state = { - showRecent: true, - showLimit: 9, - categories: [], - homeCategories: [], -}; - -const mutations = { - setShowRecent(state, payload) { - state.showRecent = payload; - }, - setShowLimit(state, payload) { - state.showLimit = payload; - }, - setCategories(state, payload) { - state.categories = payload.sort((a, b) => (a.name > b.name ? 1 : -1)); - }, - setHomeCategories(state, payload) { - state.homeCategories = payload; - }, -}; - -const actions = { - async requestHomePageSettings() { - let categories = await api.categories.get_all(); - this.commit("setCategories", categories); - }, -}; - -const getters = { - getShowRecent: state => state.showRecent, - getShowLimit: state => state.showLimit, - getCategories: state => state.categories, - getHomeCategories: state => state.homeCategories, -}; - -export default { - state, - mutations, - actions, - getters, -}; diff --git a/frontend.old/src/store/modules/language.js b/frontend.old/src/store/modules/language.js deleted file mode 100644 index c9d4ea6c4815..000000000000 --- a/frontend.old/src/store/modules/language.js +++ /dev/null @@ -1,54 +0,0 @@ -// This is the data store for the options for language selection. Property is reference only, you cannot set this property. -const state = { - allLangs: [ - { - name: "American English", - value: "en-US", - }, - { - name: "British English", - value: "en-GB", - }, - { - name: "Deutsch (German)", - value: "de-DE", - }, - { - name: "Español (Spanish)", - value: "es-ES", - }, - { - name: "Français (French)", - value: "fr-FR", - }, - { - name: "Italiano (Italian)", - value: "it-IT", - }, - { - name: "Nederlands (Dutch)", - value: "nl-NL", - }, - { - name: "Polski (Polish)", - value: "pl-PL", - }, - { - name: "Svenska (Swedish)", - value: "sv-SE", - }, - { - name: "简体中文 (Chinese simplified)", - value: "zh-CN", - }, - ], -}; - -const getters = { - getAllLangs: state => state.allLangs, -}; - -export default { - state, - getters, -}; diff --git a/frontend.old/src/store/modules/recipes.js b/frontend.old/src/store/modules/recipes.js deleted file mode 100644 index f2270eb941f2..000000000000 --- a/frontend.old/src/store/modules/recipes.js +++ /dev/null @@ -1,76 +0,0 @@ -import { api } from "@/api"; -import Vue from "vue"; -import { recipe } from "@/utils/recipe"; - -const state = { - recentRecipes: [], - allRecipes: [], -}; - -const mutations = { - setRecentRecipes(state, payload) { - state.recentRecipes = payload; - }, - patchRecentRecipes(state, payload) { - if (state.recentRecipes[payload.id]) { - state.recentRecipes[payload.id] = payload; - } - }, - dropRecentRecipes(state, payload) { - if (state.recentRecipes[payload.id]) { - Vue.delete(state.recentRecipes, payload.id); - } - }, - setAllRecipes(state, payload) { - state.allRecipes = payload; - }, - patchAllRecipes(state, payload) { - state.allRecipes[payload.id] = payload; - }, - dropAllRecipes(state, payload) { - if (state.allRecipes[payload.id]) { - Vue.delete(state.allRecipes, payload.id); - } - }, -}; - -const actions = { - async requestRecentRecipes() { - const payload = await api.recipes.allSummary(0, 30); - const hash = Object.fromEntries(payload.map(e => [e.id, e])); - this.commit("setRecentRecipes", hash); - }, - async requestAllRecipes({ getters }) { - const all = getters.getAllRecipes; - const payload = await api.recipes.allSummary(all.length, 9999); - const hash = Object.fromEntries([...all, ...payload].map(e => [e.id, e])); - - this.commit("setAllRecipes", hash); - }, - patchRecipe({ commit }, payload) { - commit("patchAllRecipes", payload); - commit("patchRecentRecipes", payload); - }, - dropRecipe({ commit }, payload) { - commit("dropAllRecipes", payload); - commit("dropRecentRecipes", payload); - }, -}; - -const getters = { - getAllRecipes: state => Object.values(state.allRecipes), - getAllRecipesHash: state => state.allRecipes, - getRecentRecipes: state => { - let list = Object.values(state.recentRecipes); - recipe.sortByUpdated(list); - return list; - }, - getRecentRecipesHash: state => state.recentRecipes, -}; - -export default { - state, - mutations, - actions, - getters, -}; diff --git a/frontend.old/src/store/modules/siteSettings.js b/frontend.old/src/store/modules/siteSettings.js deleted file mode 100644 index 3095830d0ede..000000000000 --- a/frontend.old/src/store/modules/siteSettings.js +++ /dev/null @@ -1,47 +0,0 @@ -import { api } from "@/api"; -import { loadLanguageAsync } from "@/i18n" - -const state = { - siteSettings: { - language: "en-US", - firstDayOfWeek: 0, - showRecent: true, - cardsPerSection: 9, - categories: [], - }, - customPages: [], -}; - -const mutations = { - setSettings(state, payload) { - state.siteSettings = payload; - loadLanguageAsync(payload.language); - }, - setCustomPages(state, payload) { - state.customPages = payload; - }, -}; - -const actions = { - async requestSiteSettings({ commit }) { - let settings = await api.siteSettings.get(); - commit("setSettings", settings); - }, - async requestCustomPages({ commit }) { - const customPages = await api.siteSettings.getPages(); - commit("setCustomPages", customPages); - }, -}; - -const getters = { - getActiveLang: state => state.siteSettings.language, - getSiteSettings: state => state.siteSettings, - getCustomPages: state => state.customPages, -}; - -export default { - state, - mutations, - actions, - getters, -}; diff --git a/frontend.old/src/store/modules/snackbar.js b/frontend.old/src/store/modules/snackbar.js deleted file mode 100644 index 21ae1a3982af..000000000000 --- a/frontend.old/src/store/modules/snackbar.js +++ /dev/null @@ -1,23 +0,0 @@ -const state = { - snackbar: { - open: false, - text: "Hello From The Store", - color: "info", - }, -}; - -const mutations = { - setSnackbar(state, payload) { - state.snackbar = payload; - }, -}; - -const getters = { - getSnackbar: state => state.snackbar, -}; - -export default { - state, - mutations, - getters, -}; diff --git a/frontend.old/src/store/modules/userSettings.js b/frontend.old/src/store/modules/userSettings.js deleted file mode 100644 index 39337cf93433..000000000000 --- a/frontend.old/src/store/modules/userSettings.js +++ /dev/null @@ -1,121 +0,0 @@ -import { api } from "@/api"; -import Vuetify from "@/plugins/vuetify"; -import axios from "axios"; - -function inDarkMode(payload) { - let isDark; - - if (payload === "system") { - //Get System Preference from browser - const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); - isDark = darkMediaQuery.matches; - } else if (payload === "dark") isDark = true; - else if (payload === "light") isDark = false; - - return isDark; -} - -const state = { - activeTheme: {}, - darkMode: "light", // light, dark, system - isDark: false, - isLoggedIn: false, - token: "", - userData: {}, -}; - -const mutations = { - setTheme(state, payload) { - Vuetify.framework.theme.themes.dark = payload.colors; - Vuetify.framework.theme.themes.light = payload.colors; - state.activeTheme = payload; - }, - setDarkMode(state, payload) { - let isDark = inDarkMode(payload); - - if (isDark !== null) { - Vuetify.framework.theme.dark = isDark; - state.isDark = isDark; - state.darkMode = payload; - } - }, - setIsLoggedIn(state, payload) { - state.isLoggedIn = payload; - }, - setToken(state, payload) { - state.isLoggedIn = true; - axios.defaults.headers.common["Authorization"] = `Bearer ${payload}`; - state.token = payload; - }, - - setUserData(state, payload) { - state.userData = payload; - }, -}; - -const actions = { - async requestUserData({ getters, commit }) { - if (getters.getIsLoggedIn) { - const [response, err] = await api.users.self(); - - if (err) { - return; // TODO: Log or Notifty User of Error - } - - commit("setUserData", response.data); - } - }, - - async resetTheme({ commit }) { - const defaultTheme = await api.themes.requestByName(1); - if (defaultTheme.colors) { - Vuetify.framework.theme.themes.dark = defaultTheme.colors; - Vuetify.framework.theme.themes.light = defaultTheme.colors; - commit("setTheme", defaultTheme); - } - }, - - async refreshToken({ commit, getters }) { - if (!getters.getIsLoggedIn) { - commit("setIsLoggedIn", false); // This has to be here... for some reasons? ¯\_(ツ)_/¯ - console.log("Not Logged In"); - return; - } - - const [response, err] = await api.users.refresh(); - - if (err) { - console.log("Failed Token Refresh, Logging Out..."); - commit("setIsLoggedIn", false); - } - - commit("setToken", response.data.access_token); - }, - - async initTheme({ dispatch, getters }) { - //If theme is empty resetTheme - if (Object.keys(getters.getActiveTheme).length === 0) { - await dispatch("resetTheme"); - } else { - Vuetify.framework.theme.dark = inDarkMode(getters.getDarkMode); - Vuetify.framework.theme.themes.dark = getters.getActiveTheme.colors; - Vuetify.framework.theme.themes.light = getters.getActiveTheme.colors; - } - }, -}; - -const getters = { - getActiveTheme: state => state.activeTheme, - getDarkMode: state => state.darkMode, - getIsDark: state => state.isDark, - getIsLoggedIn: state => state.isLoggedIn, - getToken: state => state.token, - getUserData: state => state.userData, -}; - -export default { - state, - mutations, - actions, - getters, -}; diff --git a/frontend.old/src/sw.js b/frontend.old/src/sw.js deleted file mode 100644 index 4b1037930aa1..000000000000 --- a/frontend.old/src/sw.js +++ /dev/null @@ -1,75 +0,0 @@ -/* eslint-disable no-undef, no-underscore-dangle, no-restricted-globals */ - -self.addEventListener("install", event => { - event.waitUntil(preLoad()); -}); - -var preLoad = async () => { - console.log("Installing web app"); - const cache = await caches.open("offline"); - console.log("caching index and important routes"); - return await cache.addAll(["/", "/recipes/all"]); -}; - -self.addEventListener("fetch", event => { - event.respondWith( - checkResponse(event.request).catch(() => { - return returnFromCache(event.request); - }) - ); - event.waitUntil(addToCache(event.request)); -}); - -var checkResponse = request => { - return new Promise(function(fulfill, reject) { - fetch(request).then(function(response) { - if (response.status !== 404) { - fulfill(response); - } else { - reject(); - } - }, reject); - }); -}; - -var addToCache = async request => { - const cache = await caches.open("offline"); - const response = await fetch(request); - console.log(response.url + " was cached"); - return await cache.put(request, response); -}; - -var returnFromCache = async request => { - const cache = await caches.open("offline"); - const matching = await cache.match(request); - if (!matching || matching.status == 404) { - return cache.match("offline.html"); - } else { - return matching; - } -}; - -// This is the code piece that GenerateSW mode can't provide for us. -// This code listens for the user's confirmation to update the app. -self.addEventListener("message", e => { - if (!e.data) { - return; - } - - switch (e.data) { - case "skipWaiting": - self.skipWaiting(); - break; - default: - // NOOP - break; - } -}); - -workbox.core.clientsClaim(); // Vue CLI 4 and Workbox v4, else -// workbox.clientsClaim(); // Vue CLI 3 and Workbox v3. - -// The precaching code provided by Workbox. -self.__precacheManifest = [].concat(self.__precacheManifest || []); -// workbox.precaching.suppressWarnings(); // Only used with Vue CLI 3 and Workbox v3. -workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); diff --git a/frontend.old/vue.config.js b/frontend.old/vue.config.js deleted file mode 100644 index 442baa0130e5..000000000000 --- a/frontend.old/vue.config.js +++ /dev/null @@ -1,50 +0,0 @@ -const path = require("path"); -const manifestJSON = require("./public/manifest.json"); -const PreloadWebpackPlugin = require("preload-webpack-plugin"); - -module.exports = { - transpileDependencies: ["vuetify"], - publicPath: process.env.NODE_ENV === "production" ? "/" : "/", - outputDir: process.env.NODE_ENV === "production" ? "./dist" : "../mealie/web", - devServer: { - proxy: { - "/api": { - target: process.env.VUE_APP_API_BASE_URL, - secure: false, - }, - }, - }, - pluginOptions: { - i18n: { - locale: "en", - fallbackLocale: "en", - localeDir: "locales", - enableInSFC: true, - }, - webpackBundleAnalyzer: { - openAnalyzer: process.env.PREVIEW_BUNDLE, - }, - }, - configureWebpack: { - resolve: { - alias: { - "@": path.resolve("src"), - }, - plugins: [new PreloadWebpackPlugin({})], - }, - }, - pwa: { - name: manifestJSON.short_name, - themeColor: manifestJSON.theme_color, - msTileColor: manifestJSON.background_color, - appleMobileWebAppCapable: "yes", - appleMobileWebAppStatusBarStyle: "black", - manifestCrossorigin: "use-credentials", - - workboxPluginMode: "InjectManifest", - workboxOptions: { - swSrc: "./src/sw.js", - swDest: "service-worker.js", - }, - }, -}; diff --git a/frontend/components/Domain/Recipe/RecipeDialogBulkAdd.vue b/frontend/components/Domain/Recipe/RecipeDialogBulkAdd.vue index bc893174ed5f..ad5385c5aff8 100644 --- a/frontend/components/Domain/Recipe/RecipeDialogBulkAdd.vue +++ b/frontend/components/Domain/Recipe/RecipeDialogBulkAdd.vue @@ -40,8 +40,8 @@ diff --git a/frontend/pages/user/profile/edit.vue b/frontend/pages/user/profile/edit.vue index cabfc34875b0..810f925fd8e0 100644 --- a/frontend/pages/user/profile/edit.vue +++ b/frontend/pages/user/profile/edit.vue @@ -73,7 +73,7 @@ - + @@ -164,28 +164,6 @@ export default defineComponent({ title: this.$t("settings.profile") as string, }; }, - - methods: { - async changePassword() { - // @ts-ignore - this.paswordLoading = true; - const data = { - currentPassword: this.password.current, - newPassword: this.password.newOne, - }; - - // @ts-ignore - if (this.$refs.passChange.validate()) { - // @ts-ignore - if (await api.users.changePassword(this.user.id, data)) { - this.$emit("refresh"); - } - } - - // @ts-ignore - this.paswordLoading = false; - }, - }, }); diff --git a/makefile b/makefile index 8763736d95b5..fe0ecc5a0ed6 100644 --- a/makefile +++ b/makefile @@ -69,14 +69,15 @@ coverage: ## ☂️ Check code coverage quickly with the default Python poetry run coverage html $(BROWSER) htmlcov/index.html +.PHONY: setup setup: ## 🏗 Setup Development Instance - cp template.env .env -n poetry install && \ cd frontend && \ - cp template.env .env -n yarn install && \ cd .. + echo "Be sure to copy the template.env files" + backend: ## 🎬 Start Mealie Backend Development Server poetry run python mealie/db/init_db.py && \ poetry run python mealie/services/image/minify.py && \ diff --git a/mealie/db/data_access_layer/access_model_factory.py b/mealie/db/data_access_layer/access_model_factory.py index 5dabf64b53ff..04fc5e79f765 100644 --- a/mealie/db/data_access_layer/access_model_factory.py +++ b/mealie/db/data_access_layer/access_model_factory.py @@ -14,11 +14,9 @@ from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUn from mealie.db.models.recipe.recipe import RecipeModel from mealie.db.models.recipe.tag import Tag from mealie.db.models.server.task import ServerTaskModel -from mealie.db.models.settings import SiteSettings from mealie.db.models.sign_up import SignUp from mealie.db.models.users import LongLiveToken, User from mealie.db.models.users.password_reset import PasswordResetModel -from mealie.schema.admin import SiteSettings as SiteSettingsSchema from mealie.schema.cookbook.cookbook import ReadCookBook from mealie.schema.events import Event as EventSchema from mealie.schema.events import EventNotificationIn @@ -94,10 +92,6 @@ class Database: # ================================================================ # Site Items - @cached_property - def settings(self) -> AccessModel[SiteSettingsSchema, SiteSettings]: - return AccessModel(self.session, pk_id, SiteSettings, SiteSettingsSchema) - @cached_property def sign_up(self) -> AccessModel[SignUpOut, SignUp]: return AccessModel(self.session, pk_id, SignUp, SignUpOut) diff --git a/mealie/db/init_db.py b/mealie/db/init_db.py index 5f960eb12dff..c63c7a946a53 100644 --- a/mealie/db/init_db.py +++ b/mealie/db/init_db.py @@ -6,7 +6,6 @@ from mealie.db.data_initialization.init_users import default_user_init from mealie.db.database import get_database from mealie.db.db_setup import create_session, engine from mealie.db.models._model_base import SqlAlchemyBase -from mealie.schema.admin import SiteSettings from mealie.schema.user.user import GroupBase from mealie.services.events import create_general_event from mealie.services.group_services.group_utils import create_new_group @@ -24,16 +23,10 @@ def create_all_models(): def init_db(db: Database) -> None: default_group_init(db) - default_settings_init(db) default_user_init(db) default_recipe_unit_init(db) -def default_settings_init(db: Database): - document = db.settings.create(SiteSettings().dict()) - logger.info(f"Created Site Settings: \n {document}") - - def default_group_init(db: Database): logger.info("Generating Default Group") create_new_group(db, GroupBase(name=settings.DEFAULT_GROUP)) diff --git a/mealie/db/models/_all_models.py b/mealie/db/models/_all_models.py index 7cb43654f344..02c4986b6a51 100644 --- a/mealie/db/models/_all_models.py +++ b/mealie/db/models/_all_models.py @@ -2,6 +2,5 @@ from .event import * from .group import * from .recipe.recipe import * from .server import * -from .settings import * from .sign_up import * from .users import * diff --git a/mealie/db/models/_model_base.py b/mealie/db/models/_model_base.py index 907eb90ca97d..fc8d93bfa39a 100644 --- a/mealie/db/models/_model_base.py +++ b/mealie/db/models/_model_base.py @@ -19,9 +19,6 @@ class BaseMixins: `cls.get_ref` method which will return the object from the database or none. Useful for many-to-many relationships. """ - class Config: - get_attr = "id" - def update(self, *args, **kwarg): self.__init__(*args, **kwarg) diff --git a/mealie/db/models/_model_utils.py b/mealie/db/models/_model_utils.py deleted file mode 100644 index c0f15881afb6..000000000000 --- a/mealie/db/models/_model_utils.py +++ /dev/null @@ -1,119 +0,0 @@ -from functools import wraps -from typing import Union - -from sqlalchemy.orm import MANYTOMANY, MANYTOONE, ONETOMANY - - -def handle_one_to_many_list(get_attr, relation_cls, all_elements: list[dict]): - elems_to_create = [] - updated_elems = [] - - for elem in all_elements: - elem_id = elem.get(get_attr, None) - - existing_elem = relation_cls.get_ref(match_value=elem_id) - - if existing_elem is None: - - elems_to_create.append(elem) - - else: - for key, value in elem.items(): - setattr(existing_elem, key, value) - - updated_elems.append(existing_elem) - - new_elems = [] - for elem in elems_to_create: - new_elems = [relation_cls(**elem) for elem in all_elements] - - return new_elems - - -def auto_init(exclude: Union[set, list] = None): # sourcery no-metrics - """Wraps the `__init__` method of a class to automatically set the common - attributes. - - Args: - exclude (Union[set, list], optional): [description]. Defaults to None. - """ - - exclude = exclude or set() - exclude.add("id") - - def decorator(init): - @wraps(init) - def wrapper(self, *args, **kwargs): # sourcery no-metrics - """ - Custom initializer that allows nested children initialization. - Only keys that are present as instance's class attributes are allowed. - These could be, for example, any mapped columns or relationships. - - Code inspired from GitHub. - Ref: https://github.com/tiangolo/fastapi/issues/2194 - """ - cls = self.__class__ - model_columns = self.__mapper__.columns - relationships = self.__mapper__.relationships - - session = kwargs.get("session", None) - - for key, val in kwargs.items(): - if key in exclude: - continue - - if not hasattr(cls, key): - continue - # raise TypeError(f"Invalid keyword argument: {key}") - - if key in model_columns: - setattr(self, key, val) - continue - - if key in relationships: - relation_dir = relationships[key].direction.name - relation_cls = relationships[key].mapper.entity - use_list = relationships[key].uselist - - try: - get_attr = relation_cls.Config.get_attr - if get_attr is None: - get_attr = "id" - except Exception: - get_attr = "id" - - if relation_dir == ONETOMANY.name and use_list: - instances = handle_one_to_many_list(get_attr, relation_cls, val) - setattr(self, key, instances) - - if relation_dir == ONETOMANY.name and not use_list: - instance = relation_cls(**val) - setattr(self, key, instance) - - elif relation_dir == MANYTOONE.name and not use_list: - if isinstance(val, dict): - val = val.get(get_attr) - - if val is None: - raise ValueError(f"Expected 'id' to be provided for {key}") - - if isinstance(val, (str, int)): - instance = relation_cls.get_ref(match_value=val, session=session) - setattr(self, key, instance) - - elif relation_dir == MANYTOMANY.name: - - if not isinstance(val, list): - raise ValueError(f"Expected many to many input to be of type list for {key}") - - if len(val) > 0 and isinstance(val[0], dict): - val = [elem.get(get_attr) for elem in val] - - instances = [x for x in [relation_cls.get_ref(elem, session=session) for elem in val] if x] - setattr(self, key, instances) - - return init(self, *args, **kwargs) - - return wrapper - - return decorator diff --git a/mealie/db/models/_model_utils/__init__.py b/mealie/db/models/_model_utils/__init__.py new file mode 100644 index 000000000000..9949173723f8 --- /dev/null +++ b/mealie/db/models/_model_utils/__init__.py @@ -0,0 +1 @@ +from .auto_init import auto_init diff --git a/mealie/db/models/_model_utils/auto_init.py b/mealie/db/models/_model_utils/auto_init.py new file mode 100644 index 000000000000..120c94803fc9 --- /dev/null +++ b/mealie/db/models/_model_utils/auto_init.py @@ -0,0 +1,184 @@ +from __future__ import annotations + +from functools import wraps + +from pydantic import BaseModel, Field +from sqlalchemy.orm import MANYTOMANY, MANYTOONE, ONETOMANY, Session +from sqlalchemy.orm.decl_api import DeclarativeMeta +from sqlalchemy.orm.mapper import Mapper +from sqlalchemy.orm.relationships import RelationshipProperty +from sqlalchemy.sql.base import ColumnCollection +from sqlalchemy.util._collections import ImmutableProperties + +from .helpers import safe_call + + +def _default_exclusion() -> set[str]: + return {"id"} + + +class AutoInitConfig(BaseModel): + """ + Config class for `auto_init` decorator. + """ + + get_attr: str = None + exclude: set = Field(default_factory=_default_exclusion) + # auto_create: bool = False + + +def _get_config(relation_cls: DeclarativeMeta) -> AutoInitConfig: + """ + Returns the config for the given class. + """ + cfg = AutoInitConfig() + cfgKeys = cfg.dict().keys() + # Get the config for the class + try: + class_config: AutoInitConfig = relation_cls.Config + except AttributeError: + return cfg + # Map all matching attributes in Config to all AutoInitConfig attributes + for attr in dir(class_config): + if attr in cfgKeys: + setattr(cfg, attr, getattr(class_config, attr)) + + return cfg + + +def get_lookup_attr(relation_cls: DeclarativeMeta) -> str: + """Returns the primary key attribute of the related class as a string. + + Args: + relation_cls (DeclarativeMeta): The SQLAlchemy class to get the primary_key from + + Returns: + Any: [description] + """ + + cfg = _get_config(relation_cls) + + try: + get_attr = cfg.get_attr + if get_attr is None: + get_attr = relation_cls.__table__.primary_key.columns.keys()[0] + except Exception: + get_attr = "id" + return get_attr + + +def handle_many_to_many(session, get_attr, relation_cls, all_elements: list[dict]): + """ + Proxy call to `handle_one_to_many_list` for many-to-many relationships. Because functionally, they do the same + """ + return handle_one_to_many_list(session, get_attr, relation_cls, all_elements) + + +def handle_one_to_many_list(session: Session, get_attr, relation_cls, all_elements: list[dict]): + elems_to_create: list[dict] = [] + updated_elems: list[dict] = [] + + for elem in all_elements: + elem_id = elem.get(get_attr, None) + + existing_elem = session.query(relation_cls).filter_by(**{get_attr: elem_id}).one_or_none() + + if existing_elem is None: + elems_to_create.append(elem) + + else: + for key, value in elem.items(): + setattr(existing_elem, key, value) + + updated_elems.append(existing_elem) + + new_elems = [safe_call(relation_cls, elem) for elem in elems_to_create] + return new_elems + updated_elems + + +def auto_init(): # sourcery no-metrics + """Wraps the `__init__` method of a class to automatically set the common + attributes. + + Args: + exclude (Union[set, list], optional): [description]. Defaults to None. + """ + + def decorator(init): + @wraps(init) + def wrapper(self: DeclarativeMeta, *args, **kwargs): # sourcery no-metrics + """ + Custom initializer that allows nested children initialization. + Only keys that are present as instance's class attributes are allowed. + These could be, for example, any mapped columns or relationships. + + Code inspired from GitHub. + Ref: https://github.com/tiangolo/fastapi/issues/2194 + """ + cls = self.__class__ + + exclude = _get_config(cls).exclude + + alchemy_mapper: Mapper = self.__mapper__ + model_columns: ColumnCollection = alchemy_mapper.columns + relationships: ImmutableProperties = alchemy_mapper.relationships + + session = kwargs.get("session", None) + + if session is None: + raise ValueError("Session is required to initialize the model with `auto_init`") + + for key, val in kwargs.items(): + if key in exclude: + continue + + if not hasattr(cls, key): + continue + # raise TypeError(f"Invalid keyword argument: {key}") + + if key in model_columns: + setattr(self, key, val) + continue + + if key in relationships: + prop: RelationshipProperty = relationships[key] + + # Identifies the type of relationship (ONETOMANY, MANYTOONE, many-to-one, many-to-many) + relation_dir = prop.direction + + # Identifies the parent class of the related object. + relation_cls: DeclarativeMeta = prop.mapper.entity + + # Identifies if the relationship was declared with use_list=True + use_list: bool = prop.uselist + + get_attr = get_lookup_attr(relation_cls) + + if relation_dir == ONETOMANY and use_list: + instances = handle_one_to_many_list(session, get_attr, relation_cls, val) + setattr(self, key, instances) + + elif relation_dir == ONETOMANY: + instance = safe_call(relation_cls, val) + setattr(self, key, instance) + + elif relation_dir == MANYTOONE and not use_list: + if isinstance(val, dict): + val = val.get(get_attr) + + if val is None: + raise ValueError(f"Expected 'id' to be provided for {key}") + + if isinstance(val, (str, int)): + instance = session.query(relation_cls).filter_by(**{get_attr: val}).one_or_none() + setattr(self, key, instance) + + elif relation_dir == MANYTOMANY: + instances = handle_many_to_many(session, get_attr, relation_cls, val) + setattr(self, key, instances) + + return init(self, *args, **kwargs) + + return wrapper + + return decorator diff --git a/mealie/db/models/_model_utils/helpers.py b/mealie/db/models/_model_utils/helpers.py new file mode 100644 index 000000000000..fa0f56c1e592 --- /dev/null +++ b/mealie/db/models/_model_utils/helpers.py @@ -0,0 +1,39 @@ +import inspect +from typing import Any, Callable + + +def get_valid_call(func: Callable, args_dict) -> dict: + """ + Returns a dictionary of valid arguemnts for the supplied function. if kwargs are accepted, + the original dictionary will be returned. + """ + + def get_valid_args(func: Callable) -> tuple: + """ + Returns a tuple of valid arguemnts for the supplied function. + """ + return inspect.getfullargspec(func).args + + def accepts_kwargs(func: Callable) -> bool: + """ + Returns True if the function accepts keyword arguments. + """ + return inspect.getfullargspec(func).varkw is not None + + if accepts_kwargs(func): + return args_dict + + valid_args = get_valid_args(func) + + return {k: v for k, v in args_dict.items() if k in valid_args} + + +def safe_call(func, dict) -> Any: + """ + Safely calls the supplied function with the supplied dictionary of arguments. + by removing any invalid arguments. + """ + try: + return func(**get_valid_call(func, dict)) + except TypeError: + return func(**dict) diff --git a/mealie/db/models/group/group.py b/mealie/db/models/group/group.py index fdc52e4cbb31..21629c055f4f 100644 --- a/mealie/db/models/group/group.py +++ b/mealie/db/models/group/group.py @@ -46,7 +46,10 @@ class Group(SqlAlchemyBase, BaseMixins): server_tasks = orm.relationship(ServerTaskModel, back_populates="group", single_parent=True) shopping_lists = orm.relationship("ShoppingList", back_populates="group", single_parent=True) - @auto_init({"users", "webhooks", "shopping_lists", "cookbooks", "preferences", "invite_tokens", "mealplans"}) + class Config: + exclude = {"users", "webhooks", "shopping_lists", "cookbooks", "preferences", "invite_tokens", "mealplans"} + + @auto_init() def __init__(self, **_) -> None: pass diff --git a/mealie/db/models/recipe/assets.py b/mealie/db/models/recipe/assets.py index 196c65c93d88..8f18a2d4dc45 100644 --- a/mealie/db/models/recipe/assets.py +++ b/mealie/db/models/recipe/assets.py @@ -11,12 +11,7 @@ class RecipeAsset(SqlAlchemyBase): icon = sa.Column(sa.String) file_name = sa.Column(sa.String) - def __init__( - self, - name=None, - icon=None, - file_name=None, - ) -> None: + def __init__(self, name=None, icon=None, file_name=None) -> None: self.name = name self.file_name = file_name self.icon = icon diff --git a/mealie/db/models/recipe/category.py b/mealie/db/models/recipe/category.py index f4f320dcbc30..7aa7aaef7ca4 100644 --- a/mealie/db/models/recipe/category.py +++ b/mealie/db/models/recipe/category.py @@ -8,12 +8,6 @@ from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase logger = root_logger.get_logger() -site_settings2categories = sa.Table( - "site_settings2categories", - SqlAlchemyBase.metadata, - sa.Column("site_settings.id", sa.Integer, sa.ForeignKey("site_settings.id")), - sa.Column("category_id", sa.Integer, sa.ForeignKey("categories.id")), -) group2categories = sa.Table( "group2categories", diff --git a/mealie/db/models/recipe/recipe.py b/mealie/db/models/recipe/recipe.py index 2a9365631027..1b4f71f40828 100644 --- a/mealie/db/models/recipe/recipe.py +++ b/mealie/db/models/recipe/recipe.py @@ -86,14 +86,7 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): class Config: get_attr = "slug" - - @validates("name") - def validate_name(self, key, name): - assert name != "" - return name - - @auto_init( - { + exclude = { "assets", "extras", "notes", @@ -103,7 +96,13 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): "settings", "tools", } - ) + + @validates("name") + def validate_name(self, key, name): + assert name != "" + return name + + @auto_init() def __init__( self, session, @@ -115,7 +114,7 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): recipe_instructions: list[dict] = None, settings: dict = None, tools: list[str] = None, - **_ + **_, ) -> None: self.nutrition = Nutrition(**nutrition) if nutrition else Nutrition() self.tools = [Tool(tool=x) for x in tools] if tools else [] diff --git a/mealie/db/models/settings.py b/mealie/db/models/settings.py deleted file mode 100644 index 79464f0bf3a2..000000000000 --- a/mealie/db/models/settings.py +++ /dev/null @@ -1,35 +0,0 @@ -import sqlalchemy as sa -import sqlalchemy.orm as orm -from sqlalchemy.orm import Session - -from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase -from mealie.db.models.recipe.category import Category, site_settings2categories - - -class SiteSettings(SqlAlchemyBase, BaseMixins): - __tablename__ = "site_settings" - id = sa.Column(sa.Integer, primary_key=True) - language = sa.Column(sa.String) - first_day_of_week = sa.Column(sa.Integer) - categories = orm.relationship("Category", secondary=site_settings2categories, single_parent=True) - show_recent = sa.Column(sa.Boolean, default=True) - cards_per_section = sa.Column(sa.Integer) - - def __init__( - self, - session: Session = None, - language="en", - first_day_of_week: int = 0, - categories: list = [], - show_recent=True, - cards_per_section: int = 9, - ) -> None: - session.commit() - self.language = language - self.first_day_of_week = first_day_of_week - self.cards_per_section = cards_per_section - self.show_recent = show_recent - self.categories = [Category.get_ref(session=session, slug=cat.get("slug")) for cat in categories] - - def update(self, *args, **kwarg): - self.__init__(*args, **kwarg) diff --git a/mealie/db/models/users/users.py b/mealie/db/models/users/users.py index 77b4f09f1367..916bf6777862 100644 --- a/mealie/db/models/users/users.py +++ b/mealie/db/models/users/users.py @@ -11,7 +11,6 @@ settings = get_app_settings() class LongLiveToken(SqlAlchemyBase, BaseMixins): __tablename__ = "long_live_tokens" - id = Column(Integer, primary_key=True) parent_id = Column(Integer, ForeignKey("users.id")) name = Column(String, nullable=False) token = Column(String, nullable=False) @@ -25,7 +24,6 @@ class LongLiveToken(SqlAlchemyBase, BaseMixins): class User(SqlAlchemyBase, BaseMixins): __tablename__ = "users" - id = Column(Integer, primary_key=True) full_name = Column(String, index=True) username = Column(String, index=True, unique=True) email = Column(String, unique=True, index=True) @@ -41,7 +39,6 @@ class User(SqlAlchemyBase, BaseMixins): can_invite = Column(Boolean, default=False) can_organize = Column(Boolean, default=False) - # Recipes tokens: list[LongLiveToken] = orm.relationship( LongLiveToken, back_populates="user", cascade="all, delete, delete-orphan", single_parent=True ) @@ -67,67 +64,52 @@ class User(SqlAlchemyBase, BaseMixins): password, favorite_recipes: list[str] = None, group: str = settings.DEFAULT_GROUP, - admin=False, advanced=False, - can_manage=False, - can_invite=False, - can_organize=False, - **_ + **kwargs ) -> None: - group = group or settings.DEFAULT_GROUP favorite_recipes = favorite_recipes or [] + self.group = Group.get_ref(session, group) + self.full_name = full_name self.email = email - self.group = Group.get_ref(session, group) - self.admin = admin self.password = password self.advanced = advanced - if self.admin: - self.can_manage = True - self.can_invite = True - self.can_organize = True - else: - self.can_manage = can_manage - self.can_invite = can_invite - self.can_organize = can_organize - self.favorite_recipes = [] if self.username is None: self.username = full_name - def update( - self, - full_name, - email, - group, - admin, - username, - session=None, - favorite_recipes=None, - password=None, - advanced=False, - can_manage=False, - can_invite=False, - can_organize=False, - **_ - ): + self._set_permissions(**kwargs) + + def update(self, full_name, email, group, username, session=None, favorite_recipes=None, advanced=False, **kwargs): favorite_recipes = favorite_recipes or [] self.username = username self.full_name = full_name self.email = email + self.group = Group.get_ref(session, group) - self.admin = admin self.advanced = advanced if self.username is None: self.username = full_name - if password: - self.password = password + self._set_permissions(**kwargs) + def update_password(self, password): + self.password = password + + def _set_permissions(self, admin, can_manage=False, can_invite=False, can_organize=False, **_): + """Set user permissions based on the admin flag and the passed in kwargs + + Args: + admin (bool): + can_manage (bool): + can_invite (bool): + can_organize (bool): + """ + self.admin = admin if self.admin: self.can_manage = True self.can_invite = True @@ -137,9 +119,6 @@ class User(SqlAlchemyBase, BaseMixins): self.can_invite = can_invite self.can_organize = can_organize - def update_password(self, password): - self.password = password - @staticmethod def get_ref(session, id: str): return session.query(User).filter(User.id == id).one() diff --git a/mealie/routes/site_settings/site_settings.py b/mealie/routes/site_settings/site_settings.py index 23e272e526cd..01dda81a7539 100644 --- a/mealie/routes/site_settings/site_settings.py +++ b/mealie/routes/site_settings/site_settings.py @@ -5,7 +5,6 @@ from mealie.core.dependencies import get_current_user from mealie.db.database import get_database from mealie.db.db_setup import generate_session from mealie.routes.routers import AdminAPIRouter -from mealie.schema.admin import SiteSettings from mealie.schema.user import GroupInDB, PrivateUser from mealie.utils.post_webhooks import post_webhooks @@ -21,16 +20,6 @@ def get_main_settings(session: Session = Depends(generate_session)): return db.settings.get(1) -@admin_router.put("") -def update_settings( - data: SiteSettings, - session: Session = Depends(generate_session), -): - """ Returns Site Settings """ - db = get_database(session) - db.settings.update(1, data.dict()) - - @admin_router.post("/webhooks/test") def test_webhooks( current_user: PrivateUser = Depends(get_current_user), diff --git a/mealie/routes/users/passwords.py b/mealie/routes/users/passwords.py index 7986a517fe7e..c760b31359c4 100644 --- a/mealie/routes/users/passwords.py +++ b/mealie/routes/users/passwords.py @@ -24,10 +24,9 @@ async def reset_user_password(id: int, session: Session = Depends(generate_sessi db.users.update_password(id, new_password) -@user_router.put("/{id}/password") +@user_router.put("/{item_id}/password") def update_password(password_change: ChangePassword, user_service: UserService = Depends(UserService.write_existing)): """ Resets the User Password""" - return user_service.change_password(password_change) diff --git a/mealie/schema/admin/settings.py b/mealie/schema/admin/settings.py index 4002db51bb8f..31468754bb3f 100644 --- a/mealie/schema/admin/settings.py +++ b/mealie/schema/admin/settings.py @@ -4,31 +4,7 @@ from fastapi_camelcase import CamelModel from pydantic import validator from slugify import slugify -from ..recipe.recipe_category import CategoryBase, RecipeCategoryResponse - - -class SiteSettings(CamelModel): - language: str = "en-US" - first_day_of_week: int = 0 - show_recent: bool = True - cards_per_section: int = 9 - categories: Optional[list[CategoryBase]] = [] - - class Config: - orm_mode = True - - schema_extra = { - "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"}, - ], - } - } +from ..recipe.recipe_category import RecipeCategoryResponse class CustomPageBase(CamelModel): diff --git a/mealie/services/backups/imports.py b/mealie/services/backups/imports.py index bf511e3f39d4..3c483bc957b1 100644 --- a/mealie/services/backups/imports.py +++ b/mealie/services/backups/imports.py @@ -11,15 +11,7 @@ from mealie.core.config import get_app_dirs app_dirs = get_app_dirs() from mealie.db.database import get_database -from mealie.schema.admin import ( - CommentImport, - GroupImport, - NotificationImport, - RecipeImport, - SettingsImport, - SiteSettings, - UserImport, -) +from mealie.schema.admin import CommentImport, GroupImport, NotificationImport, RecipeImport, UserImport from mealie.schema.events import EventNotificationIn from mealie.schema.recipe import CommentOut, Recipe from mealie.schema.user import PrivateUser, UpdateGroup @@ -181,19 +173,7 @@ class ImportDatabase: return import_notifications def import_settings(self): - settings_file = self.import_dir.joinpath("settings", "settings.json") - settings = ImportDatabase.read_models_file(settings_file, SiteSettings) - settings = settings[0] - - try: - self.db.settings.update(1, settings.dict()) - import_status = SettingsImport(name="Site Settings", status=True) - - except Exception as inst: - self.session.rollback() - import_status = SettingsImport(name="Site Settings", status=False, exception=str(inst)) - - return [import_status] + return [] def import_groups(self): groups_file = self.import_dir.joinpath("groups", "groups.json") diff --git a/mealie/services/migrations/_migration_base.py b/mealie/services/migrations/_migration_base.py index 78659be25681..f39ea2fd1901 100644 --- a/mealie/services/migrations/_migration_base.py +++ b/mealie/services/migrations/_migration_base.py @@ -181,7 +181,7 @@ class MigrationBase(BaseModel): except Exception as inst: exception = inst - logger.error(inst) + logger.exception(inst) self.session.rollback() import_status = MigrationImport(slug=recipe.slug, name=recipe.name, status=status, exception=str(exception)) diff --git a/mealie/services/user_services/user_service.py b/mealie/services/user_services/user_service.py index 9b8fb38a006e..59f5c692ae2d 100644 --- a/mealie/services/user_services/user_service.py +++ b/mealie/services/user_services/user_service.py @@ -13,7 +13,12 @@ class UserService(UserHttpService[int, str]): event_func = create_user_event acting_user: PrivateUser = None + def populate_item(self, item_id: int) -> None: + self.acting_user = self.db.users.get_one(item_id) + return self.acting_user + def assert_existing(self, id) -> PrivateUser: + self.populate_item(id) self._populate_target_user(id) self._assert_user_change_allowed() return self.target_user @@ -32,7 +37,6 @@ class UserService(UserHttpService[int, str]): self.target_user = self.acting_user def change_password(self, password_change: ChangePassword) -> PrivateUser: - """""" if not verify_password(password_change.current_password, self.target_user.password): raise HTTPException(status.HTTP_400_BAD_REQUEST) diff --git a/poetry.lock b/poetry.lock index 8ccdf36004f4..a66ab7c783de 100644 --- a/poetry.lock +++ b/poetry.lock @@ -41,7 +41,7 @@ six = "*" [[package]] name = "apscheduler" -version = "3.8.0" +version = "3.8.1" description = "In-process task scheduler with Cron-like capabilities" category = "main" optional = false @@ -50,7 +50,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.dependencies] pytz = "*" six = ">=1.4.0" -tzlocal = ">=2.0,<3.0" +tzlocal = ">=2.0,<3.0.0 || >=4.0.0" [package.extras] asyncio = ["trollius"] @@ -67,7 +67,7 @@ zookeeper = ["kazoo"] [[package]] name = "astroid" -version = "2.8.3" +version = "2.8.4" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false @@ -646,7 +646,7 @@ i18n = ["babel (>=2.9.0)"] [[package]] name = "mkdocs-material" -version = "7.3.4" +version = "7.3.6" description = "A Material Design theme for MkDocs" category = "dev" optional = false @@ -657,7 +657,7 @@ jinja2 = ">=2.11.1" markdown = ">=3.2" mkdocs = ">=1.2.3" mkdocs-material-extensions = ">=1.0" -pygments = ">=2.4" +pygments = ">=2.10" pymdown-extensions = ">=9.0" [[package]] @@ -691,14 +691,14 @@ signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "packaging" -version = "21.0" +version = "21.2" description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -pyparsing = ">=2.0.2" +pyparsing = ">=2.0.2,<3" [[package]] name = "passlib" @@ -1044,6 +1044,17 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "pytz-deprecation-shim" +version = "0.1.0.post0" +description = "Shims to make deprecation of pytz easier" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" + +[package.dependencies] +tzdata = {version = "*", markers = "python_version >= \"3.6\""} + [[package]] name = "pyyaml" version = "5.4.1" @@ -1263,16 +1274,29 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "tzdata" +version = "2021.5" +description = "Provider of IANA time zone data" +category = "main" +optional = false +python-versions = ">=2" + [[package]] name = "tzlocal" -version = "2.1" +version = "4.1" description = "tzinfo object for the local timezone" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" [package.dependencies] -pytz = "*" +pytz-deprecation-shim = "*" +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] +test = ["pytest-mock (>=3.3)", "pytest (>=4.3)"] [[package]] name = "urllib3" @@ -1370,7 +1394,7 @@ python-versions = ">=3.6.1" [[package]] name = "wrapt" -version = "1.13.2" +version = "1.13.3" description = "Module for decorators, wrappers and monkey patching." category = "dev" optional = false @@ -1414,12 +1438,12 @@ apprise = [ {file = "apprise-0.9.3.tar.gz", hash = "sha256:c8ace9c891d4224558570acbea808f31df9049530bd9ccd69901c27a65d600c4"}, ] apscheduler = [ - {file = "APScheduler-3.8.0-py2.py3-none-any.whl", hash = "sha256:82d6d21b2f0343510d07bb35618333a794653439a265635555b12935647b460a"}, - {file = "APScheduler-3.8.0.tar.gz", hash = "sha256:793b2d37c52ece53e34626619e6142e99b20b59a12155f39e1e6932e324f079d"}, + {file = "APScheduler-3.8.1-py2.py3-none-any.whl", hash = "sha256:c22cb14b411a31435eb2c530dfbbec948ac63015b517087c7978adb61b574865"}, + {file = "APScheduler-3.8.1.tar.gz", hash = "sha256:5cf344ebcfbdaa48ae178c029c055cec7bc7a4a47c21e315e4d1f08bd35f2355"}, ] astroid = [ - {file = "astroid-2.8.3-py3-none-any.whl", hash = "sha256:f9d66e3a4a0e5b52819b2ff41ac2b179df9d180697db71c92beb33a60c661794"}, - {file = "astroid-2.8.3.tar.gz", hash = "sha256:0e361da0744d5011d4f5d57e64473ba9b7ab4da1e2d45d6631ebd67dd28c3cce"}, + {file = "astroid-2.8.4-py3-none-any.whl", hash = "sha256:0755c998e7117078dcb7d0bda621391dd2a85da48052d948c7411ab187325346"}, + {file = "astroid-2.8.4.tar.gz", hash = "sha256:1e83a69fd51b013ebf5912d26b9338d6643a55fec2f20c787792680610eed4a2"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, @@ -1820,12 +1844,28 @@ markdown = [ {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, ] markupsafe = [ + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, @@ -1834,14 +1874,27 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, @@ -1851,6 +1904,12 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, @@ -1871,8 +1930,8 @@ mkdocs = [ {file = "mkdocs-1.2.3.tar.gz", hash = "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1"}, ] mkdocs-material = [ - {file = "mkdocs-material-7.3.4.tar.gz", hash = "sha256:9cc0ea54933322792855150ffb89401ff4eaf7737aadca5155fa679195ad803a"}, - {file = "mkdocs_material-7.3.4-py2.py3-none-any.whl", hash = "sha256:6a9c5a2f18a39ac0b6af73d23c452cdb1f9a07bf6365d43ee54cb796b1d2c7eb"}, + {file = "mkdocs-material-7.3.6.tar.gz", hash = "sha256:1b1dbd8ef2508b358d93af55a5c5db3f141c95667fad802301ec621c40c7c217"}, + {file = "mkdocs_material-7.3.6-py2.py3-none-any.whl", hash = "sha256:1b6b3e9e09f922c2d7f1160fe15c8f43d4adc0d6fb81aa6ff0cbc7ef5b78ec75"}, ] mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, @@ -1887,8 +1946,8 @@ oauthlib = [ {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"}, ] packaging = [ - {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, - {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, + {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"}, + {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"}, ] passlib = [ {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, @@ -1959,6 +2018,11 @@ premailer = [ ] psycopg2-binary = [ {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"}, + {file = "psycopg2_binary-2.9.1-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:24b0b6688b9f31a911f2361fe818492650795c9e5d3a1bc647acbd7440142a4f"}, + {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:542875f62bc56e91c6eac05a0deadeae20e1730be4c6334d8f04c944fcd99759"}, + {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661509f51531ec125e52357a489ea3806640d0ca37d9dada461ffc69ee1e7b6e"}, + {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:d92272c7c16e105788efe2cfa5d680f07e34e0c29b03c1908f8636f55d5f915a"}, + {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:736b8797b58febabb85494142c627bd182b50d2a7ec65322983e71065ad3034c"}, {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"}, {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"}, {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"}, @@ -2106,6 +2170,10 @@ pytz = [ {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, ] +pytz-deprecation-shim = [ + {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, + {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, +] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, @@ -2113,18 +2181,26 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, @@ -2221,6 +2297,8 @@ sqlalchemy = [ {file = "SQLAlchemy-1.4.26-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c757ba1279b85b3460e72e8b92239dae6f8b060a75fb24b3d9be984dd78cfa55"}, {file = "SQLAlchemy-1.4.26-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:c24c01dcd03426a5fe5ee7af735906bec6084977b9027a3605d11d949a565c01"}, {file = "SQLAlchemy-1.4.26-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c46f013ff31b80cbe36410281675e1fb4eaf3e25c284fd8a69981c73f6fa4cb4"}, + {file = "SQLAlchemy-1.4.26-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fb2aa74a6e3c2cebea38dd21633671841fbe70ea486053cba33d68e3e22ccc0a"}, + {file = "SQLAlchemy-1.4.26-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7e403fc1e3cb76e802872694e30d6ca6129b9bc6ad4e7caa48ca35f8a144f8"}, {file = "SQLAlchemy-1.4.26-cp310-cp310-win32.whl", hash = "sha256:7ef421c3887b39c6f352e5022a53ac18de8387de331130481cb956b2d029cad6"}, {file = "SQLAlchemy-1.4.26-cp310-cp310-win_amd64.whl", hash = "sha256:908fad32c53b17aad12d722379150c3c5317c422437e44032256a77df1746292"}, {file = "SQLAlchemy-1.4.26-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:1ef37c9ec2015ce2f0dc1084514e197f2f199d3dc3514190db7620b78e6004c8"}, @@ -2298,9 +2376,13 @@ typing-extensions = [ {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, ] +tzdata = [ + {file = "tzdata-2021.5-py2.py3-none-any.whl", hash = "sha256:3eee491e22ebfe1e5cfcc97a4137cd70f092ce59144d81f8924a844de05ba8f5"}, + {file = "tzdata-2021.5.tar.gz", hash = "sha256:68dbe41afd01b867894bbdfd54fa03f468cfa4f0086bfb4adcd8de8f24f3ee21"}, +] tzlocal = [ - {file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"}, - {file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"}, + {file = "tzlocal-4.1-py3-none-any.whl", hash = "sha256:28ba8d9fcb6c9a782d6e0078b4f6627af1ea26aeaa32b4eab5324abc7df4149f"}, + {file = "tzlocal-4.1.tar.gz", hash = "sha256:0f28015ac68a5c067210400a9197fc5d36ba9bc3f8eaf1da3cbd59acdfed9e09"}, ] urllib3 = [ {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, @@ -2390,50 +2472,57 @@ websockets = [ {file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"}, ] wrapt = [ - {file = "wrapt-1.13.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3de7b4d3066cc610054e7aa2c005645e308df2f92be730aae3a47d42e910566a"}, - {file = "wrapt-1.13.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:8164069f775c698d15582bf6320a4f308c50d048c1c10cf7d7a341feaccf5df7"}, - {file = "wrapt-1.13.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9adee1891253670575028279de8365c3a02d3489a74a66d774c321472939a0b1"}, - {file = "wrapt-1.13.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a70d876c9aba12d3bd7f8f1b05b419322c6789beb717044eea2c8690d35cb91b"}, - {file = "wrapt-1.13.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3f87042623530bcffea038f824b63084180513c21e2e977291a9a7e65a66f13b"}, - {file = "wrapt-1.13.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e634136f700a21e1fcead0c137f433dde928979538c14907640607d43537d468"}, - {file = "wrapt-1.13.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3e33c138d1e3620b1e0cc6fd21e46c266393ed5dae0d595b7ed5a6b73ed57aa0"}, - {file = "wrapt-1.13.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:283e402e5357e104ac1e3fba5791220648e9af6fb14ad7d9cc059091af2b31d2"}, - {file = "wrapt-1.13.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ccb34ce599cab7f36a4c90318697ead18312c67a9a76327b3f4f902af8f68ea1"}, - {file = "wrapt-1.13.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:fbad5ba74c46517e6488149514b2e2348d40df88cd6b52a83855b7a8bf04723f"}, - {file = "wrapt-1.13.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:724ed2bc9c91a2b9026e5adce310fa60c6e7c8760b03391445730b9789b9d108"}, - {file = "wrapt-1.13.2-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:83f2793ec6f3ef513ad8d5b9586f5ee6081cad132e6eae2ecb7eac1cc3decae0"}, - {file = "wrapt-1.13.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:0473d1558b93e314e84313cc611f6c86be779369f9d3734302bf185a4d2625b1"}, - {file = "wrapt-1.13.2-cp35-cp35m-win32.whl", hash = "sha256:15eee0e6fd07f48af2f66d0e6f2ff1916ffe9732d464d5e2390695296872cad9"}, - {file = "wrapt-1.13.2-cp35-cp35m-win_amd64.whl", hash = "sha256:bc85d17d90201afd88e3d25421da805e4e135012b5d1f149e4de2981394b2a52"}, - {file = "wrapt-1.13.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6ee5f8734820c21b9b8bf705e99faba87f21566d20626568eeb0d62cbeaf23c"}, - {file = "wrapt-1.13.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:53c6706a1bcfb6436f1625511b95b812798a6d2ccc51359cd791e33722b5ea32"}, - {file = "wrapt-1.13.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fbe6aebc9559fed7ea27de51c2bf5c25ba2a4156cf0017556f72883f2496ee9a"}, - {file = "wrapt-1.13.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:0582180566e7a13030f896c2f1ac6a56134ab5f3c3f4c5538086f758b1caf3f2"}, - {file = "wrapt-1.13.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bff0a59387a0a2951cb869251257b6553663329a1b5525b5226cab8c88dcbe7e"}, - {file = "wrapt-1.13.2-cp36-cp36m-win32.whl", hash = "sha256:df3eae297a5f1594d1feb790338120f717dac1fa7d6feed7b411f87e0f2401c7"}, - {file = "wrapt-1.13.2-cp36-cp36m-win_amd64.whl", hash = "sha256:1eb657ed84f4d3e6ad648483c8a80a0cf0a78922ef94caa87d327e2e1ad49b48"}, - {file = "wrapt-1.13.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0cdedf681db878416c05e1831ec69691b0e6577ac7dca9d4f815632e3549580"}, - {file = "wrapt-1.13.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:87ee3c73bdfb4367b26c57259995935501829f00c7b3eed373e2ad19ec21e4e4"}, - {file = "wrapt-1.13.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3e0d16eedc242d01a6f8cf0623e9cdc3b869329da3f97a15961d8864111d8cf0"}, - {file = "wrapt-1.13.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:8318088860968c07e741537030b1abdd8908ee2c71fbe4facdaade624a09e006"}, - {file = "wrapt-1.13.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d90520616fce71c05dedeac3a0fe9991605f0acacd276e5f821842e454485a70"}, - {file = "wrapt-1.13.2-cp37-cp37m-win32.whl", hash = "sha256:22142afab65daffc95863d78effcbd31c19a8003eca73de59f321ee77f73cadb"}, - {file = "wrapt-1.13.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d0d717e10f952df7ea41200c507cc7e24458f4c45b56c36ad418d2e79dacd1d4"}, - {file = "wrapt-1.13.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:593cb049ce1c391e0288523b30426c4430b26e74c7e6f6e2844bd99ac7ecc831"}, - {file = "wrapt-1.13.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8860c8011a6961a651b1b9f46fdbc589ab63b0a50d645f7d92659618a3655867"}, - {file = "wrapt-1.13.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ada5e29e59e2feb710589ca1c79fd989b1dd94d27079dc1d199ec954a6ecc724"}, - {file = "wrapt-1.13.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:fdede980273aeca591ad354608778365a3a310e0ecdd7a3587b38bc5be9b1808"}, - {file = "wrapt-1.13.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:af9480de8e63c5f959a092047aaf3d7077422ded84695b3398f5d49254af3e90"}, - {file = "wrapt-1.13.2-cp38-cp38-win32.whl", hash = "sha256:c65e623ea7556e39c4f0818200a046cbba7575a6b570ff36122c276fdd30ab0a"}, - {file = "wrapt-1.13.2-cp38-cp38-win_amd64.whl", hash = "sha256:b20703356cae1799080d0ad15085dc3213c1ac3f45e95afb9f12769b98231528"}, - {file = "wrapt-1.13.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c5c4cf188b5643a97e87e2110bbd4f5bc491d54a5b90633837b34d5df6a03fe"}, - {file = "wrapt-1.13.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:82223f72eba6f63eafca87a0f614495ae5aa0126fe54947e2b8c023969e9f2d7"}, - {file = "wrapt-1.13.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:81a4cf257263b299263472d669692785f9c647e7dca01c18286b8f116dbf6b38"}, - {file = "wrapt-1.13.2-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:728e2d9b7a99dd955d3426f237b940fc74017c4a39b125fec913f575619ddfe9"}, - {file = "wrapt-1.13.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:7574de567dcd4858a2ffdf403088d6df8738b0e1eabea220553abf7c9048f59e"}, - {file = "wrapt-1.13.2-cp39-cp39-win32.whl", hash = "sha256:c7ac2c7a8e34bd06710605b21dd1f3576764443d68e069d2afba9b116014d072"}, - {file = "wrapt-1.13.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e6d1a8eeef415d7fb29fe017de0e48f45e45efd2d1bfda28fc50b7b330859ef"}, - {file = "wrapt-1.13.2.tar.gz", hash = "sha256:dca56cc5963a5fd7c2aa8607017753f534ee514e09103a6c55d2db70b50e7447"}, + {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"}, + {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"}, + {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"}, + {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"}, + {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"}, + {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"}, + {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"}, + {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"}, + {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"}, + {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"}, + {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"}, + {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"}, + {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"}, + {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"}, + {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"}, + {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"}, + {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"}, + {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"}, + {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"}, + {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"}, + {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"}, + {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"}, + {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"}, + {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"}, + {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"}, + {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"}, + {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"}, + {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"}, + {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"}, + {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"}, + {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"}, + {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"}, + {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"}, + {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"}, + {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"}, + {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"}, + {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"}, + {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"}, + {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"}, ] zipp = [ {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, diff --git a/tests/integration_tests/test_settings_routes.py b/tests/integration_tests/test_settings_routes.py deleted file mode 100644 index f245c82729d5..000000000000 --- a/tests/integration_tests/test_settings_routes.py +++ /dev/null @@ -1,32 +0,0 @@ -import json - -import pytest -from fastapi.testclient import TestClient - -from mealie.schema.admin import SiteSettings -from tests.app_routes import AppRoutes - - -@pytest.fixture(scope="function") -def default_settings(): - return SiteSettings().dict(by_alias=True) - - -def test_default_settings(api_client: TestClient, api_routes: AppRoutes, default_settings): - response = api_client.get(api_routes.site_settings) - - assert response.status_code == 200 - - assert json.loads(response.content) == default_settings - - -def test_update_settings(api_client: TestClient, api_routes: AppRoutes, default_settings, admin_token): - default_settings["language"] = "fr" - default_settings["showRecent"] = False - - response = api_client.put(api_routes.site_settings, json=default_settings, headers=admin_token) - - assert response.status_code == 200 - - response = api_client.get(api_routes.site_settings) - assert json.loads(response.content) == default_settings