Compare commits

...

385 Commits

Author SHA1 Message Date
Hayden
13850cda1f security: multiple reported CVE fixes (#1515)
* update out of date license

* update typing / refactor

* fix arbitrarty path injection

* use markdown sanatizer to prevent XSS CWE-79

* fix CWE-918 SSRF by validating url and mime type

* add security docs

* update recipe-scrapers

* resolve DOS from arbitrary url

* update changelog

* bump version

* add ref to #1506

* add #1511 to changelog

* use requests decoder

* actually fix encoding issue
2022-07-31 13:10:20 -08:00
Michael Genson
483f789b8e feat: create new foods and units from their Data Management pages (#1511)
* added create dialogs to food and unit pages

* minor css tweaks

* properly reset create form

* added placeholder name attribute for type checking

* removed unnecessary value assignment

* type fixes

* corrected comment

* add autofocus and use ref<VForm> for form refs

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-07-31 12:31:20 -08:00
Michael Genson
1b83c82997 feat: implement local storage for sorting and dynamic sort icons on the new recipe sort card (#1506)
* added new sort icons

* added dynamic sort icons

* implemented local storage for sorting
and mobile card view

* fixed bug with local storage booleans

* added type hints

* bum vue use to use merge defaults

* use reactive localstorage

* add $vuetify type

* sort returns

* fix type error

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-07-31 11:39:35 -08:00
Philipp Fischbeck
34f52c06a6 fix: properly use pagination for group event notifiers (#1512) 2022-07-31 10:08:48 -08:00
Michael Genson
07fef8af9f feat: restore frontend sorting for all recipes (#1497)
* fixed typo

* merged "all recipes" pagination into recipe card
created custom sort card for all recipes
refactored backend calls for all recipes to sort/paginate

* frontend lint fixes

* restored recipes reference

* replaced "this" with reference

* fix linting errors

* re-order context menu

* add todo

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-07-26 18:08:56 -08:00
Michael Genson
703ee32653 docs: pagination and filtering, and fixed a few broken links (#1488)
* fixed broken links

* added docs for pagination and filtering

* small revision to pagination response example
2022-07-26 17:45:34 -08:00
Michael Genson
3d4e5441dd chore: backend page_all route cleanup (#1483)
* refactored to remove duplicate code

* refactored meal plan slice to use a query filter
2022-07-26 17:43:25 -08:00
Hayden
f00280e32b New Crowdin updates (#1480)
* New translations en-US.json (Dutch)

* New translations en-US.json (Czech)

* New translations en-US.json (German)
2022-07-26 17:41:33 -08:00
Hayden
9e6a720cf1 New Crowdin updates (#1455)
* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (French, Canada)

* New translations en-US.json (French, Canada)

* New translations en-US.json (French, Canada)
2022-07-09 21:17:34 -08:00
Michael Genson
7f50071312 feat: advanced filtering API (#1468)
* created query filter classes

* extended pagination to include query filtering

* added filtering tests

* type improvements

* move type help to dev depedency

* minor type and perf fixes

* breakup test cases

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-07-09 20:57:09 -08:00
Miroito
c64da1fdb7 Feature: Toggle display of ingredient references in recipe instructions (#1268)
* Better cooking mode

* Fix wrong event sent

* feat/cookmode recipe page integration

* implement scaling in cook mode + minor padding

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-07-09 20:28:34 -08:00
Michael Genson
2809cef3b1 fix: mealplan pagination (#1464)
* added pagination to get_slice route

* updated mealplan tests

* renamed vars to match pagination query
2022-07-02 09:44:01 -08:00
Benjamin Pabst
2f7ff6d178 fix: use mtime instead of ctime for backup dates (#1461) 2022-06-27 07:57:09 -08:00
Hayden
c05e048b65 docs: fix old link 2022-06-26 19:39:35 -08:00
Hayden
157bad0e29 fix: use == operator instead of is_ for sql queries (#1453) 2022-06-26 12:42:13 -08:00
Hayden
f96a584a5d New Crowdin updates (#1452)
* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Danish)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Greek)

* New translations en-US.json (Greek)
2022-06-26 11:21:57 -08:00
Miroito
151e20489a ui: Improve parser ui text display (#1437)
move text display when open to be below the ingredient portion
2022-06-26 11:20:38 -08:00
Hayden
7dbb0858bd New Crowdin updates (#1439)
* New translations en-US.json (Dutch)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)
2022-06-25 12:20:44 -08:00
Hayden
b921e95163 fix: entry nutrition checker (#1448) 2022-06-25 12:19:04 -08:00
Michael Genson
cb15db2d27 feat: re-write get all routes to use pagination (#1424)
rewrite get_all routes to use a pagination pattern to allow for better implementations of search, filter, and sorting on the frontend or by any client without fetching all the data. Additionally we added a CI check for running the Nuxt built to confirm that no TS errors were present. Finally, I had to remove the header support for the Shopping lists as the browser caching based off last_updated header was not allowing it to read recent updates due to how we're handling the updated_at property in the database with nested fields. This will have to be looked at in the future to reimplement. I'm unsure how many other routes have a similar issue. 

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-06-25 11:39:38 -08:00
Philipp Fischbeck
c158672d12 fix: add missing types for API token deletion (#1428) 2022-06-21 09:42:03 -08:00
Michael Genson
292bf7068a feat: added "last-modified" header to supported record types (#1379)
* fixed type error

* exposed created/updated timestamps to shopping list schema

* added custom route to mix in "last-modified" header when available in CRUD routes

* mixed in MealieCrudRoute to APIRouters

* added HEAD route for shopping lists/list-items

* replaced default serializer with FastAPI's
2022-06-21 09:41:14 -08:00
Hayden
5db4dedc3f hotfix: tame typescript beast 2022-06-20 16:48:39 -08:00
Hayden
f122c382e9 add recipe.image cache key to bush caches (#1427)
* add recipe.image cache key to bush caches

* hotfix: TS type error
2022-06-19 11:47:16 -08:00
Hayden
c865bc7769 fix: only show scaler when ingredients amounts enabled (#1426) 2022-06-19 10:27:32 -08:00
Michael Genson
efffe26a19 fix: sort recent recipes by created_at instead of date_added (#1417)
* added staticmethod decorators to avoid mypy error

* exposed created and updated timestamps to schema

* changed default sort from date_added to created_at

* explicitely sort recent recipes by created_at

* removed static method and replaced w/ type: ignore
2022-06-19 10:08:26 -08:00
Hayden
8b054fd945 New Crowdin updates (#1406)
* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)
2022-06-19 10:03:39 -08:00
Michael Genson
bb1fa52d10 fix: all-recipes page now sorts alphabetically (#1405)
* added sort params to backend call

* hardcoded alphabetical sort param

* removed trivial type annotation

* linters are friends, not food
2022-06-19 10:03:24 -08:00
Hayden
d4b92a8ade revert i18n version 2022-06-17 14:13:18 -08:00
dependabot[bot]
85d514eb1a chore(deps-dev): bump @vue/runtime-dom in /frontend (#1423)
Bumps [@vue/runtime-dom](https://github.com/vuejs/core/tree/HEAD/packages/runtime-dom) from 3.2.36 to 3.2.37.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.2.37/packages/runtime-dom)

---
updated-dependencies:
- dependency-name: "@vue/runtime-dom"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-17 13:47:12 -08:00
dependabot[bot]
8878f78ab1 fix(deps): bump core-js from 3.17.2 to 3.23.1 in /frontend (#1383)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.17.2 to 3.23.1.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/compare/v3.17.2...v3.23.1)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-17 13:45:55 -08:00
dependabot[bot]
d315ad63d2 fix(deps): bump fuse.js from 6.5.3 to 6.6.2 in /frontend (#1325)
Bumps [fuse.js](https://github.com/krisk/Fuse) from 6.5.3 to 6.6.2.
- [Release notes](https://github.com/krisk/Fuse/releases)
- [Changelog](https://github.com/krisk/Fuse/blob/master/CHANGELOG.md)
- [Commits](https://github.com/krisk/Fuse/compare/v6.5.3...v6.6.2)

---
updated-dependencies:
- dependency-name: fuse.js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-17 13:45:24 -08:00
dependabot[bot]
48053b55b9 fix(deps): bump date-fns from 2.23.0 to 2.28.0 in /frontend (#1293)
Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.23.0 to 2.28.0.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md)
- [Commits](https://github.com/date-fns/date-fns/compare/v2.23.0...v2.28.0)

---
updated-dependencies:
- dependency-name: date-fns
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-17 13:36:33 -08:00
dependabot[bot]
78c7399ff7 fix(deps): bump @nuxtjs/i18n from 7.0.3 to 7.2.2 in /frontend (#1288)
Bumps [@nuxtjs/i18n](https://github.com/nuxt-community/i18n-module) from 7.0.3 to 7.2.2.
- [Release notes](https://github.com/nuxt-community/i18n-module/releases)
- [Changelog](https://github.com/nuxt-community/i18n-module/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/i18n-module/compare/v7.0.3...v7.2.2)

---
updated-dependencies:
- dependency-name: "@nuxtjs/i18n"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-17 13:35:21 -08:00
dependabot[bot]
f70fc18222 fix(deps): bump @mdi/js from 5.9.55 to 6.7.96 in /frontend (#1279)
Bumps [@mdi/js](https://github.com/Templarian/MaterialDesign-JS) from 5.9.55 to 6.7.96.
- [Release notes](https://github.com/Templarian/MaterialDesign-JS/releases)
- [Commits](https://github.com/Templarian/MaterialDesign-JS/compare/v5.9.55...v6.7.96)

---
updated-dependencies:
- dependency-name: "@mdi/js"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-17 13:35:08 -08:00
Hayden
6f83b0f522 chore: bump dev deps (#1418) 2022-06-17 13:34:22 -08:00
Hayden
5a053cdcd6 feat: mealplan-webhooks (#1403)
* fix type errors on event bus

* webhooks fields required for new implementation

* db migration

* wip: webhook query + tests and stub function

* ignore type checker error

* type and method cleanup

* datetime and time utc validator

* update testing code for utc scheduled time

* fix file cmp function call

* update version_number

* add support for translating "time" objects when restoring backup

* bump recipe-scrapers

* use specific import syntax

* generate frontend types

* utilize names exports

* use utc times

* add task to scheduler

* implement new scheduler functionality

* stub for type annotation

* implement meal-plan data getter

* add experimental banner
2022-06-17 13:25:47 -08:00
Hayden
b1256f4ad2 fix: fast fail of bulk importer (#1394)
* use continue instead of break

* catch additional error case

* spelling is hard
2022-06-15 18:19:52 -08:00
Hayden
525842e9a1 New Crowdin updates (#1392)
* New translations en-US.json (Ukrainian)

* New translations en-US.json (French)

* New translations en-US.json (Italian)

* New translations en-US.json (German)

* New translations en-US.json (Danish)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Ukrainian)
2022-06-15 18:19:36 -08:00
Michael Genson
9e261f5235 fix: infinite scroll bug on all recipes page (#1393) 2022-06-15 12:56:56 -08:00
Jim Eagle
3f808f8f00 docs: add go bulk import example (#1388)
* Fix link
* Add go bulk import
2022-06-15 11:50:19 -08:00
Hayden
394df6c210 New Crowdin updates (#1375)
* New translations en-US.json (French)

* New translations en-US.json (French)
2022-06-15 11:50:01 -08:00
Michael Genson
754e77c9cb feat: extend Apprise JSON notification functionality with programmatic data (#1355)
* Fixed incorrect generic deleted notification text

* Added custom "event_source" header for json notifs

* Added internal reference data to event notifs

* Added event listeners to shopping list items

* Fixed type issues

* moved JSON event source k:v pairs to message body

* added hook for all supported custom endpoints
fixed bug that excluded non-custom notification types

* created event_source class to replace loosely-typed dict

* fixed silent error when dispatching a null task

* moved url updates to static function

* added unit tests for event_source url manipulation

* removed array from event bus (it's unsupported)
2022-06-15 11:49:42 -08:00
Hayden
3030e3e7f4 feat: implement user favorites page (#1376)
* fix geFavorites return

* add support for toggling to dense cards on desktop

* add favorites page link

* implement basic favorites page
2022-06-13 09:33:46 -08:00
Michael Genson
f6c18ec73d fix avoid page breaks in sections when printing recipes and other CSS tweaks (#1372)
* grouped ingredients and instructions into sections

* added missing import

* divided ingredient sections and instruction sections into their own containers

* tweaked css to prevent sections from getting split between pages

* replaced horizontal rule with a text underline

* removed leftover CSS

* implement computer properties as reducers

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-06-12 16:43:09 -08:00
Hayden
84dc60d7bf New translations en-US.json (Danish) (#1371) 2022-06-11 09:57:05 -08:00
Michael Genson
7541175b75 feat: implemented "order by" API parameters for recipe, food, and unit queries (#1356)
* Added API params to order by different properties

* fix for incorrect var name

* removed invalid default order_by

* implemented fallback for invalid user input
2022-06-11 09:56:55 -08:00
Hayden
932f4a72df refactor: remove depreciated repo call (#1370)
* ingredient parser hot fixes (float equality)

* remove `get` in favor of `get_one` & `multi_query`
2022-06-10 19:01:14 -08:00
Michael Genson
b904b161eb fix: increased float rounding precision for CRF parser (#1369)
* increased float rounding precision for crf parser

* limited fractions to a max denominator of 32 to prevent weirdly specific values

* add test cases for 1/8 and 1/32

* add rounding to avoid more digits than necessary

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-06-10 18:18:31 -08:00
Michael Genson
504bf41b9c fix: Ingredient sections lost after parsing (#1368)
* fixed bug where ingredient titles were lost after parsing

* added fallback in case of strange behavior during parsing

* removed unnecessary linebreak
2022-06-10 18:17:51 -08:00
Michael Genson
92ccbae657 fix: fixed text color on RecipeCard in RecipePrintView and implemented ingredient sections (#1351)
* Enhanced ingredients in RecipePrintView

* Resolved frontend lint tests

* switched lets to consts and simplified import

* implement with CSS grid

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-06-10 16:48:07 -08:00
Hayden
c0d59db83d New Crowdin updates (#1365)
* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)
2022-06-10 16:38:19 -08:00
Hayden
511ce91630 New Crowdin updates (#1364)
* New translations en-US.json (German)

* New translations en-US.json (Korean)

* New translations en-US.json (English, United Kingdom)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Turkish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Slovak)

* New translations en-US.json (Russian)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Japanese)

* New translations en-US.json (French)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Finnish)

* New translations en-US.json (Greek)

* New translations en-US.json (Danish)

* New translations en-US.json (Czech)

* New translations en-US.json (Catalan)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Arabic)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Spanish)

* New translations en-US.json (Romanian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Italian)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Ukrainian)
2022-06-09 10:16:09 -08:00
Michael Genson
5f5eb2c46d fix: for erroneously-translated datetime config (#1362)
* Fix for erroneously-translated datetime config

* remove datetime formats from crowdin

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-06-09 08:54:41 -08:00
Michael Genson
4662253d0e Fixed alpha sort in RecipeOrganizerPage (#1354) 2022-06-09 08:50:03 -08:00
Benjamin Pabst
8836a258bd feat: add custom scaling option (#1345)
* Added custom scaling option

* Allow custom scaling with no yield set

* Made edit-scale translated

* fixed merge conflict

* Refactored scale editor to use menu

* replaced vslot with #

* linter issues

* fixed linter issues

* fixed one more linter issue

* format files + minor UI changes

* remove console.log

* move buttons into component and setup v-model

* drop servings text

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-06-09 08:01:25 -08:00
Hayden
56eb0bca71 release: prep beta 3 (#1349)
* docs: add tag documentation

* bump docker-compose tags

* minor language changes

* beta3 changelog

* auto-gen API docs

* bump version

* bump recipe-scrapers
2022-06-07 11:19:24 -08:00
Hayden
eca8a96509 fix image display - closes #1189 (#1348) 2022-06-05 11:44:00 -08:00
Hayden
7eb80d18d2 feat: attached images by drag and drop for recipe steps (#1341)
* add drag and drop support for recipe steps

* fix recipe assets dialog state

* add attr support for markdown editor

* add persistent hint for recipe text editor
2022-06-05 11:28:38 -08:00
Benjamin Pabst
37a673b34d Update postgres to use most recent version (#1347) 2022-06-05 10:56:01 -08:00
Benjamin Pabst
3e7b8d4b71 Update to use most recent version (#1346) 2022-06-05 10:55:33 -08:00
Jurjen de Jonge
abb114c375 security: delay server response whenever username is non existing (#1338)
* Delay server response whenever username is non existing

* utilize hasher to achieve constant timing

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-06-04 10:27:30 -08:00
Hayden
12f480eb75 refactor: unify recipe-organizer components (#1340)
* use generic context menu

* implement organizer stores

* add basic organizer types

* refactor selectors to apply for all organizers

* remove legacy organizer composables
2022-06-03 20:12:32 -08:00
Hayden
bc175d4ca9 New translations en-US.json (Ukrainian) (#1339) 2022-06-03 19:49:58 -08:00
Hayden
f78c5eb359 New Crowdin updates (#1329)
* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (Ukrainian)
2022-06-02 09:12:15 -08:00
Hayden
5a0c034391 fix: shopping list label editor (#1333)
* remove head props

* lazily compute itemsByLabel with watcher on fetch

* remove unused import
2022-06-02 09:12:05 -08:00
Hayden
52fbf6b833 feat: add unit abbreviation support (#1332)
* add 'use-abbreviation' db column

* type generation

* add view and edit elements

* check for use_abbreviation to display

* fix: alembic version check

* test: add use_abbreviation prop tests
2022-06-01 11:59:50 -08:00
dependabot[bot]
592b1de39d fix(deps): bump @vue/composition-api from 1.6.1 to 1.6.2 in /frontend (#1275)
Bumps [@vue/composition-api](https://github.com/vuejs/composition-api) from 1.6.1 to 1.6.2.
- [Release notes](https://github.com/vuejs/composition-api/releases)
- [Changelog](https://github.com/vuejs/composition-api/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/composition-api/compare/v1.6.1...v1.6.2)

---
updated-dependencies:
- dependency-name: "@vue/composition-api"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-31 08:48:57 -08:00
dependabot[bot]
f29d5f1dff chore(deps-dev): bump @babel/eslint-parser in /frontend (#1290)
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.15.4 to 7.18.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.2/eslint/babel-eslint-parser)

---
updated-dependencies:
- dependency-name: "@babel/eslint-parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-31 08:48:33 -08:00
dependabot[bot]
738ef0aaa7 chore(deps-dev): bump @types/sortablejs in /frontend (#1287)
Bumps [@types/sortablejs](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sortablejs) from 1.10.7 to 1.13.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/sortablejs)

---
updated-dependencies:
- dependency-name: "@types/sortablejs"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-31 08:48:01 -08:00
Hayden
f1fdec5afe add security.md 2022-05-31 08:29:45 -08:00
Hayden
4c594a48dc wip: pagination-repository (#1316)
* bump mypy

* add pagination + refactor generic repo

* add pagination test

* remove all query object
2022-05-30 10:30:54 -08:00
Hayden
00f144a622 chore: init git-cliff config 2022-05-29 17:34:41 -08:00
Hayden
d2a9f7ca24 fix: consoldate stores to fix mismatched state 2022-05-29 17:34:41 -08:00
Hayden
f831791db2 feat: default unit fractions to True 2022-05-29 17:34:41 -08:00
Hayden
c3bdfe7b3b fix: printer page refs (#1314) 2022-05-29 09:15:38 -08:00
Hayden
3542bb0927 fix: bad import path (#1313)
* fix bad import

* add eslint rule for incorrect imports
2022-05-29 09:09:36 -08:00
Philipp Fischbeck
e898c80f59 fix: update issue links in v1.0.0beta-2 changelog (#1312) 2022-05-29 08:51:11 -08:00
James Addison
27c5cfc56b Fixup: render homepage social media link images at 32x32 size (#1310) 2022-05-29 08:50:44 -08:00
Hayden
369cda0a61 New Crowdin updates (#1308)
* New translations en-US.json (Italian)

* New translations en-US.json (Italian)
2022-05-29 08:50:18 -08:00
Hayden
b981cf62bf chore: bump version (#1307)
* bump version

* add release notes
2022-05-28 17:13:36 -08:00
Hayden
ee93d77ace Update stale.yml
exclude tasks
2022-05-28 17:04:40 -08:00
Hayden
3dcfcc1fa9 feat: rewrite print implementation to support new ing (#1305) 2022-05-28 17:00:37 -08:00
Hayden
80f1a9add8 New Crowdin updates (#1304)
* New translations en-US.json (French)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (German)
2022-05-28 16:38:59 -08:00
Hayden
137bf9de91 bump recipe scrapers version (#1303) 2022-05-28 16:38:47 -08:00
Drumstickx
1534f0df77 docs: docker-compose.dev.yml is currently not functional (#1300) 2022-05-27 16:17:47 -08:00
Drumstickx
d751e3b35b docs: add references for VSCode dev containers (#1299) 2022-05-27 16:17:39 -08:00
Hayden
07bf5be3ec improve touch support with icon and handle (#1302) 2022-05-27 16:17:25 -08:00
Hayden
a96f94a149 fix: add touch support for mealplanner delete (#1298)
* add touch support for mealplanner delete

* remove click.stop
2022-05-26 19:31:54 -08:00
Hayden
78a8204b58 New translations en-US.json (German) (#1296) 2022-05-26 09:01:17 -08:00
Hayden
649e34f66e add touch support for mealplanner (#1295) 2022-05-25 20:13:13 -08:00
Hayden
010aafa69b feat: add reports to bulk recipe import (url) (#1294)
* remove unused docker and caddy configs

* add experimental nested configs

* switch to nest under docker-compose

* remove v-card

* bulk parser backend re-implementation

* refactor UI for bulk importer

* remove migration specific report text
2022-05-25 19:33:58 -08:00
Hayden
d66d6c55ae fix recipe assets build (#1286) 2022-05-25 11:50:45 -08:00
Hayden
7609715d9e remove explicity typescript version (#1285)
* remove explicity typescript version

* i hate javascript
2022-05-25 10:14:24 -08:00
Hayden
921fceddea chore: update dev dependencies (#1282)
* update dev dependencies

* upgrade eslint

* resolve several errors

* resolve eslint errors
2022-05-25 09:38:21 -08:00
Hayden
01f3fef21f New Crowdin updates (#1284)
* New translations en-US.json (German)

* New translations en-US.json (German)
2022-05-25 09:08:41 -08:00
Philipp Fischbeck
8f7c7c39bb refactor: split up recipe create page (#1283)
* refactor: split up recipe create page

* add flat card

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-05-25 09:08:32 -08:00
Hayden
30d19c6503 fix: bad dev dependency (#1281)
* revert bad dev dependency

* fix concurrent builds
2022-05-24 21:19:50 -08:00
Hayden
ea503a0235 add conccureny config (#1280) 2022-05-24 20:24:33 -08:00
dependabot[bot]
c05c123880 fix(deps): bump @nuxtjs/auth-next in /frontend (#1265)
Bumps [@nuxtjs/auth-next](https://github.com/nuxt-community/auth-module) from 5.0.0-1624817847.21691f1 to 5.0.0-1648802546.c9880dc.
- [Release notes](https://github.com/nuxt-community/auth-module/releases)
- [Changelog](https://github.com/nuxt-community/auth-module/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/auth-module/commits)

---
updated-dependencies:
- dependency-name: "@nuxtjs/auth-next"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-24 20:02:43 -08:00
dependabot[bot]
6f45de6167 chore(deps-dev): bump vue2-script-setup-transform in /frontend (#1263)
Bumps [vue2-script-setup-transform](https://github.com/antfu/vue2-script-setup-transform) from 0.2.6 to 0.3.5.
- [Release notes](https://github.com/antfu/vue2-script-setup-transform/releases)
- [Commits](https://github.com/antfu/vue2-script-setup-transform/compare/v0.2.6...v0.3.5)

---
updated-dependencies:
- dependency-name: vue2-script-setup-transform
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-24 20:02:00 -08:00
dependabot[bot]
d634e2fbe1 chore(deps-dev): bump nuxt-vite from 0.1.3 to 0.3.5 in /frontend (#1260)
Bumps [nuxt-vite](https://github.com/nuxt/vite) from 0.1.3 to 0.3.5.
- [Release notes](https://github.com/nuxt/vite/releases)
- [Changelog](https://github.com/nuxt/vite/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nuxt/vite/compare/v0.1.3...v0.3.5)

---
updated-dependencies:
- dependency-name: nuxt-vite
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-24 20:00:50 -08:00
dependabot[bot]
43a566339a chore(deps-dev): bump @vue/runtime-dom in /frontend (#1259)
Bumps [@vue/runtime-dom](https://github.com/vuejs/core/tree/HEAD/packages/runtime-dom) from 3.2.35 to 3.2.36.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.2.36/packages/runtime-dom)

---
updated-dependencies:
- dependency-name: "@vue/runtime-dom"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-24 20:00:16 -08:00
dependabot[bot]
cc284a0ceb chore(deps-dev): bump eslint-plugin-nuxt in /frontend (#1258)
Bumps [eslint-plugin-nuxt](https://github.com/nuxt/eslint-plugin-nuxt) from 2.0.0 to 3.2.0.
- [Release notes](https://github.com/nuxt/eslint-plugin-nuxt/releases)
- [Changelog](https://github.com/nuxt/eslint-plugin-nuxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nuxt/eslint-plugin-nuxt/compare/v2.0.0...v3.2.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-nuxt
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-24 19:59:49 -08:00
dependabot[bot]
3c19105d8b fix(deps): bump isomorphic-dompurify from 0.18.0 to 0.19.0 in /frontend (#1257)
Bumps [isomorphic-dompurify](https://github.com/kkomelin/isomorphic-dompurify) from 0.18.0 to 0.19.0.
- [Release notes](https://github.com/kkomelin/isomorphic-dompurify/releases)
- [Commits](https://github.com/kkomelin/isomorphic-dompurify/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: isomorphic-dompurify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-24 19:58:14 -08:00
Hayden
b8ee1a4bd8 fix #1270 migration not capture settings (#1272) 2022-05-24 09:55:15 -08:00
Hayden
c30ffbc851 update readme 2022-05-24 09:51:39 -08:00
Hayden
3ddbc033b2 chore: github stalebot changes (#1271)
* add exempt labels

* update tags for docker-compose
2022-05-24 08:30:07 -08:00
Hayden
ca0d7e1ea2 chore: github chores (#1267)
* grab `dev` branch templates

* add beta-release workflow
2022-05-23 21:53:54 -08:00
Hayden
66a8d1860b Docs/update release notes (#1254)
* remove vetur config

* auto generate docs

* add latest changes

* cleanup feature pages
2022-05-23 21:30:58 -08:00
Hayden
8235bd5104 chore: bump deps (#1252)
* bump deps

* fix: add apt-get update
2022-05-22 11:52:57 -08:00
Hayden
cd0da36e7c fix: recipe ingredient editor bugs (#1251)
* filter unallowed fields #1140

* fix type and layout

* propery validate none type quantites

* fix rendering error #1237
2022-05-22 11:16:23 -08:00
Hayden
d06d4d2fd9 New Crowdin updates (#1250)
* New translations en-US.json (Italian)

* New translations en-US.json (Danish)
2022-05-22 11:14:36 -08:00
Hayden
a85fa3fc54 add null check for source directory (#1248) 2022-05-21 11:50:01 -08:00
Hayden
89d609e47b New Crowdin updates (#1247)
* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Romanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (English, United Kingdom)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Turkish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Slovak)

* New translations en-US.json (Russian)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Korean)

* New translations en-US.json (French)

* New translations en-US.json (Japanese)

* New translations en-US.json (Italian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Finnish)

* New translations en-US.json (Greek)

* New translations en-US.json (German)

* New translations en-US.json (Danish)

* New translations en-US.json (Czech)

* New translations en-US.json (Catalan)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Arabic)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Spanish)

* New translations en-US.json (French, Canada)
2022-05-21 11:22:40 -08:00
Philipp Fischbeck
479900e912 Consolidate frontend types (#1245) 2022-05-21 11:22:02 -08:00
Nick Kringle
6a88a59981 feat: extend email support for SSL/No Auth Email Support (#1235)
* Changes Settings to use new SMTP_AUTH_STRATEGY variable in place of SMTP_TLS with transition support

#1187

* Wires up default email client to use ssl or tls authentication if enabled in settings

* Updates the docs

* Update template file

* remove SMTP_TLS and use staticmethod for validate

* consolidate test cases with params

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-05-21 11:15:14 -08:00
Hayden
b2066dfe72 feat: add initial notification support
* Add updated recipe notification

* Add recipe deleted notification

* Add notifications translations

* Shopping lists full c/u/d notifications

* Add categories c/u/d notifications

* Deal with None values in translation provider

* Add tag c/u/d notifications

* Add cookbook c/u/d notifications

* use single key pairs for consistency with frontend

* change dependency injection strategy

* use generic update messages

* use service to manage url generation server-side

* use new strategies for messages

* fix translator

Co-authored-by: Miroito <alban.vachette@gmail.com>
2022-05-21 10:23:55 -08:00
Hayden
841b560abc New Crowdin updates (#1214)
* New translations en-US.json (Romanian)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (English, United Kingdom)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Turkish)

* New translations en-US.json (French)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Spanish)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Arabic)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Catalan)

* New translations en-US.json (Czech)

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Swedish)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

* New translations en-US.json (Korean)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovak)

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Slovak)

* New translations en-US.json (French, Canada)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (English, United Kingdom)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Turkish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Slovak)

* New translations en-US.json (Russian)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Korean)

* New translations en-US.json (French)

* New translations en-US.json (Japanese)

* New translations en-US.json (Italian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Finnish)

* New translations en-US.json (Greek)

* New translations en-US.json (German)

* New translations en-US.json (Danish)

* New translations en-US.json (Czech)

* New translations en-US.json (Catalan)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Arabic)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Spanish)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)
2022-05-15 15:30:49 -08:00
Philipp Fischbeck
4d3346b727 Fix #1205 by using isomorphic-dompurify (#1228) 2022-05-15 15:30:33 -08:00
Miroito
3125730702 Improve ingredient sections behavior (#1233) 2022-05-15 15:30:18 -08:00
Hayden
4fe19b88ca ux: unify UI based on user-feedback (#1216)
* unify UI based on user-feedback

* fix layout shify error

* implement drag and drop animation
2022-05-11 17:14:03 -08:00
Hayden
8f1c082d79 Update Crowdin configuration file 2022-05-10 14:44:40 -08:00
Hayden
bdbeba1a81 Change Mealie license to AGPLv3 (#1213) 2022-05-09 13:11:26 -08:00
Hayden
c65ef4e4c4 changelog 2022-05-09 13:11:07 -08:00
Hayden
74441c48be docs: features section and change log update 2022-05-09 13:01:19 -08:00
Hayden
1fefd40623 feat: show-recipe-scraper-version (#1210)
* add recipe scraper version

* add scraper version and details copy dialog

* implement axios downloader - fix #1171
2022-05-08 17:43:03 -08:00
Miroito
7d498c5274 Remove obsolete default settings test (#1204) 2022-05-07 20:10:10 -08:00
Hayden
8448c26b09 New translations en-US.json (French) (#1209) 2022-05-07 20:09:57 -08:00
Hayden
68f7efc177 fix #1144 (#1207)
* fix #1144

* fix type checks

* refactor test routes package

* fix #1208

* unify test routes into module
2022-05-07 20:08:04 -08:00
Hayden
07f6446526 add food and unit seeding UI (#1206) 2022-05-06 12:17:30 -08:00
Hayden
7e4da3e5a4 feat: improved registration signup flow (#1188)
refactored signup flow for entire registration process. Utilized seed data option for optional seeding of Foods, Units, and Labels. Localized registration page.
2022-05-06 11:18:06 -08:00
Hayden
6ee9a31c92 New Crowdin updates (#1200)
* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)
2022-05-05 21:35:56 -08:00
Hayden
41c26692b2 New Crowdin updates (#1182)
* New translations en-US.json (French)

* New translations en-US.json (French, Canada)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (Italian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (German)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (Japanese)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Turkish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Slovak)

* New translations en-US.json (Russian)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Korean)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Finnish)

* New translations en-US.json (Greek)

* New translations en-US.json (Czech)

* New translations en-US.json (Catalan)

* New translations en-US.json (Arabic)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Spanish)

* New translations en-US.json (French)

* New translations en-US.json (Romanian)

* New translations en-US.json (Danish)

* New translations en-US.json (Italian)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (English, United Kingdom)
2022-05-03 12:10:22 -08:00
zierbeek
5111a440ff Activate Navigation.instant (#1191)
Change edit_uri to be able to edit the nightly docs
remove demo_url since not needed

 Changes to be committed:
	modified:   ios.md
	modified:   ../../../mkdocs.yml
2022-05-02 08:35:31 -08:00
Hayden
d6e2b4ab85 Feature/user seedable foods (#1176)
* remove odd ingredients

* UI Elements for food

* update translated percentage

* spek -> speck

* generate types

* seeder api endpoints + tests

* implement foods seeder UI

* localize some food strings
2022-05-01 12:45:50 -08:00
Miroito
67178f9b74 fix: error on duplicate username/email registration (#1168)
* fix: error on duplicate username/email registration

* Raise 409 http error on duplicate user registration

* Add translation to conflict error messages

* Update lang files

* Remove key in translator parameter
2022-04-30 12:25:29 -08:00
Hayden
633ec541ea New Crowdin updates (#1178)
* New translations en-US.json (German)

* New translations en-US.json (German)
2022-04-30 12:23:24 -08:00
Hayden
33c4af8c1c New Crowdin updates (#1174)
* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)
2022-04-26 12:12:12 -08:00
Miroito
f1e84a7769 fix:Error when scraper returns None (#1175) 2022-04-26 12:11:59 -08:00
Hayden
2613420cd1 security: fix unsafe html inputs (#1173)
* use DomPurify to sanatize ingredient html

* fix list-item render for markdown

* address volar linter issue
2022-04-24 13:00:04 -08:00
Drumstickx
1c41ce7538 Fix typos and wording in v1.0.0 docs (#1172) 2022-04-24 10:35:50 -08:00
Hayden
683d4a26dc build hotfix 2022-04-23 14:30:58 -08:00
Hayden
c696dee320 Feature/import keywords as tags (#1170)
* feat: import original keywords as tags

* remove cached env

* Update frontend api types

* fix: Issues with previous tag scraping implementation

* Update category handling in backend

* Update backend tests to include group_id

* Correct type check

* Update create-url interface

* Improve tag cleaner list support

* remove builtin name shadowing

* update type annotations

* test include tags scraper

* implement scaper context for optional data

* readd cache venv

* use continue instead of break

* remove test callback

Co-authored-by: Miroito <alban.vachette@gmail.com>
2022-04-23 12:23:12 -08:00
Hayden
75c535fb2e resolve #1092 (#1169) 2022-04-23 10:41:16 -08:00
Miroito
60682dba75 Allow tags/categories/tools deletion (#1142)
* feat: allow tags/categories/tools deletion

* Use gray color for delete action
2022-04-20 10:09:23 -08:00
Miroito
aff30adda6 Fix failed tests when env default email/password were changed (#1157)
* fix: failed tests when env default email/password changed

* Remove default email exposition in docs
2022-04-20 10:07:46 -08:00
Hayden
8831c2ec85 New Crowdin updates (#1160)
* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (German)
2022-04-20 09:55:29 -08:00
Hayden
288194eb9d set disable_amount to default to true (#1158) 2022-04-19 16:49:53 -08:00
Hayden
e9c66af275 New Crowdin updates (#1155)
* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (German)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (French, Canada)

* New translations en-US.json (French, Canada)

* New translations en-US.json (French, Canada)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)
2022-04-19 10:56:06 -08:00
Hayden
2cd6594c4d New Crowdin updates (#1153)
* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Greek)

* New translations en-US.json (Greek)

* New translations en-US.json (Greek)

* New translations en-US.json (Swedish)

* New translations en-US.json (Danish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Hungarian)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (German)
2022-04-13 13:19:44 -08:00
Hayden
ea3db3c3d5 New Crowdin updates (#1151)
* New translations en-US.json (Romanian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Greek)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Finnish)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (German)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Korean)

* New translations en-US.json (Korean)

* New translations en-US.json (Korean)

* New translations en-US.json (Dutch)

* New translations en-US.json (Greek)

* New translations en-US.json (German)

* New translations en-US.json (Romanian)

* New translations en-US.json (Arabic)

* New translations en-US.json (Romanian)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Arabic)

* New translations en-US.json (German)

* New translations en-US.json (Arabic)

* New translations en-US.json (Catalan)

* New translations en-US.json (Catalan)

* New translations en-US.json (Catalan)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Turkish)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (English, United Kingdom)

* New translations en-US.json (English, United Kingdom)

* New translations en-US.json (English, United Kingdom)

* New translations en-US.json (French, Canada)

* New translations en-US.json (French, Canada)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Turkish)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Russian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Russian)

* New translations en-US.json (Turkish)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (French, Canada)

* New translations en-US.json (French, Canada)
2022-04-10 18:51:02 -08:00
Hayden
045798e959 chore: drop-apscheduler (#1152)
* rewrite interval timer

* drop apscheduler

* fix type annotations
2022-04-10 18:13:38 -08:00
Hayden
4f55020a58 chore: move locales to folder (#1150)
* move locales to own folder

* fix file references
2022-04-10 14:30:06 -08:00
Hayden
b87594a52c New Crowdin updates (#1149)
* New translations en-US.json (Romanian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (English, United Kingdom)

* New translations en-US.json (Portuguese, Brazilian)

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Chinese Traditional)

* New translations en-US.json (Chinese Simplified)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Turkish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Serbian (Cyrillic))

* New translations en-US.json (Slovak)

* New translations en-US.json (Russian)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Dutch)

* New translations en-US.json (French)

* New translations en-US.json (Korean)

* New translations en-US.json (Japanese)

* New translations en-US.json (Italian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Finnish)

* New translations en-US.json (Greek)

* New translations en-US.json (German)

* New translations en-US.json (Danish)

* New translations en-US.json (Czech)

* New translations en-US.json (Catalan)

* New translations en-US.json (Arabic)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Spanish)

* New translations en-US.json (French, Canada)
2022-04-10 14:29:17 -08:00
Hayden
7866f0f46e Feature/improve localization (#1147)
* use locale to set language header

* rewrite i18n provider and drop dependency

* rename file

* rename CrudMixin to HttpRepo

* refactor: code-cleanup

* add crowdin source

* remove unused translations

* grab translations from dev branch

* add translation support for foods, units, and labels

* remove rich import
2022-04-10 14:07:35 -08:00
Hayden
db095656e1 add merge funcions for units (#1146) 2022-04-09 19:57:49 -08:00
Hayden
b93dae109e feat: merge food into another (#1143)
* setup food repository

* add merge route and payloads

* remove type checking

* generate types

* implement merge dialog

* food repo tests

* split install from workflow

* bum dependencies

* revert changes

* update copy

* refactor URLs to avoid incorrect template being used

* stick advanced items under developer mode

* use utility component for advanced feature
2022-04-09 19:08:48 -08:00
Hayden
10784b6e24 feat: support require_all property for cookbooks (#1130)
* add direction prop for icon position

* add support for require_all properties on cookbook

* update type annotations

* add and - or filter support

* update cookbook API

* generate types

* implement editor for additional options

* update version number
2022-04-03 16:32:58 -08:00
Hayden
c988de1921 fix: group creation (#1126)
* fix: unify group creation - closes #1100

* tests: disable password hashing during testing

* tests: fix email config tests
2022-04-02 19:33:15 -08:00
Hayden
e9bb39c744 feat: docker volume validation (#1125)
* feat: add api endpoints for volume check

* feat: add docker icon

* add size prop

* feat: add frontend UI for checking docker-volume

* update caddy to server validation file

* add more extensive documentation around setup req

* fix: wrong type on user id #1123

* spelling

* refactor: cleanup excessive function calls
2022-04-02 16:35:53 -08:00
zierbeek
ea141832c3 Update swag documentation (#1119) 2022-04-02 16:07:58 -08:00
Hayden
3e3b43c977 feat: add on mounted hook for bookmarklets (#1120)
* add utility type to hide Advanced items

* add default text

* fix #1115 and minor refactorings

* generate types

* hotfix: add validator to catch null vlaues
2022-04-01 11:05:25 -08:00
Hayden
cfaac2e060 feat: additional cookbook features (tags, tools, and public) (#1116)
* migration: add public, tags, and tools

* generate frontend types

* add help icon

* start replacement for tool-tag-category selector

* add help icon utility

* use generator types

* add support for cookbook features

* add UI elements for cookbook features

* fix tests

* fix type error
2022-04-01 09:50:31 -08:00
Hayden
1092e0ce7c feat: add peak toggle for seeing original ingredient txt (#1111)
* add peak toggle for seeing original ingredient txt

* add recent changes + format

* cleanup search view

* space out results
2022-03-29 09:38:23 -08:00
Hayden
1a23f867da feat: admin maintenance and analytics stubs (#1107)
* add tail log viewer routes

* add log viewer

* add _mealie to ignore directories

* add detailed breakdown of storage

* generate types

* add dialog to view breakdown

* cleanup mobile UI

* move migrations page

* spelling

* init analytics page

* move route up

* add remove temp files function

* analytics API client

* stub out analytics pages

* generate types

* stub out analytics routes

* update names

* ignore types

* temporary remove analytics from sidebar
2022-03-29 08:25:28 -08:00
Miroito
6f309d7a89 feat: keep original text on ingredient parse (#1102)
* Keep Original Text on Ingredient Parse
* Reorder migration and update test
2022-03-29 08:07:01 -08:00
Miroito
5e44d1c238 Fix clear url action in recipe creation (#1101) 2022-03-28 07:32:26 -08:00
Hayden
1e90dc2022 feat: add group statistics on profile page
* resolve file not found error and add constants

* add group stats and storage functionality

* generate new types

* add statistics and storage cap graphs

* fix: add loadFood query param #1103

* refactor to flex view
2022-03-27 15:12:18 -08:00
Hayden
b57e42a3b3 Fix/incorrect quantity column (#1093)
* change database type

* database migration for changing type on PostgreSQL

* update revision

* add exclusion directory

* update recipe-scrapers
2022-03-27 09:18:34 -08:00
Hayden
7895dfba70 hot-fix: remove old task 2022-03-25 16:23:28 -08:00
Hayden
596d0200dc collect recent changes (#1099) 2022-03-25 15:09:56 -08:00
Hayden
11b4d2389a chore: refactor base schema (#1098)
* remove dead backup code

* implmenet own base model

* refactor to use MealieModel instead of CamelModel

* cleanup deps
2022-03-25 10:56:49 -08:00
Hayden
bcd98cba2f fix: args order (#1097) 2022-03-25 09:06:19 -08:00
Hayden
4ef649231b feat: admin maintenance page (#1096)
* fix build typo

* generate types

* setup maintenance api for common cleanup actions

* admin maintenance page

* remove duplicate use-with-caution
2022-03-24 22:17:38 -08:00
Hayden
ffb3b45ac2 feat: add build id from commit hash (#1095)
* fix old ref

* capture git commit as build ID

* generate new types

* display build on Site Settings page

* fix extras crash it extras is none
2022-03-24 20:29:01 -08:00
Hayden
7f102f513d Feature/auto increment recipe name (#1088)
* auto-increment-recipe-name

* add test-case

* re-implement as try/except
2022-03-23 17:23:40 -08:00
Hayden
0f82523cdd Fix/category link (#1087)
* fix and refactor chip links

* fix missing UI components for data management

* fix button text
2022-03-22 21:08:06 -08:00
Hayden
ba325c12f7 Fix/fix broken pwa (#1086)
* remove fetch / use axios fix #1077

* revert checkbox change

* add password peek

* fix bool check
2022-03-22 20:41:54 -08:00
Miroito
e743d2c66b Reorganize Group/User Page Routes (#1084)
* Consolidate group routes

* Update doc migration link
2022-03-22 19:54:16 -08:00
Hayden
20822ee808 Feature/improve user feedback (#1076)
* add proper type annotations

* fix state management and dead code

* add response messages
2022-03-20 16:12:49 -08:00
Hayden
eb2aadfe9e use retry loop on startup (#1075) 2022-03-20 16:12:37 -08:00
Hayden
50a67f9301 Feature/auto generate crowdin data (#1071)
* add translated key

* update code generation for crowdin generation

* use composition api and minor styling changes
2022-03-19 16:33:55 -08:00
Philipp Fischbeck
022cbd1616 Enable localization based on browser settings, add language selector (#925)
* Enable localization based on browser settings, add language selector

* Add dialog for language selection
2022-03-19 13:27:40 -08:00
Hayden
8f569509bf Feature/move label editor (#1069)
* update default color

* move labels editor
2022-03-19 11:31:17 -08:00
Hayden
8c0c8be659 Feature/group items editor (#1064)
* update types

* remove toolbox routes

* remove unused ""

* add generic crud table

* update calls for type safety

* recreate food/unit editors

* fix type error

* remove shopping list link

* add transition

* add basic search box

* conditional show-select

* styling + basic download support

* generic download as json function

* add fraction support

* add export option

* add label text
2022-03-17 10:30:10 -08:00
Daniel Palstra
86b450fb8c Improve devcontainer and configure VSCode debugging (#1060) 2022-03-16 08:37:56 -08:00
Hayden
960378b213 hotfix: run fetch client side only 2022-03-15 19:31:45 -08:00
Hayden
13e157827c Fix/fix block registration (#1059)
* fix disable button

* add backend env for restricting registration

* update state management

* add allow_signup to app info

* move allow_signup to backend only

* cleanup docker-compose

* potential darkmode fix

* fix missing variable

* add banner on login page

* use random bools for tests

* fix initial state bug

* fix state reset
2022-03-15 17:34:53 -08:00
Hayden
3c2744a3da improve developer tooling (backend) (#1051)
* add basic pre-commit file

* add flake8

* add isort

* add pep585-upgrade (typing upgrades)

* use namespace for import

* add mypy

* update ci for backend

* flake8 scope

* fix version format

* update makefile

* disable strict option (temporary)

* fix mypy issues

* upgrade type hints (pre-commit)

* add vscode typing check

* add types to dev deps

* remote container draft

* update setup script

* update compose version

* run setup on create

* dev containers update

* remove unused pages

* update setup tips

* expose ports

* Update pre-commit to include flask8-print (#1053)

* Add in flake8-print to pre-commit

* pin version of flake8-print

* formatting

* update getting strated docs

* add mypy to pre-commit

* purge .mypy_cache on clean

* drop mypy

Co-authored-by: zackbcom <zackbcom@users.noreply.github.com>
2022-03-15 15:01:56 -08:00
Matthias Bilger
e109391e9a Added validators for users and recipes (#1052)
* Added validators for users and recipes

provide a simple get api, allowing to test for existence of
- user by username
- recipe by slug
- group by name (not tested yet)

* updated formatting

* Use group_id+slug for recipes, use ValidationRespone

* Fixed Flake8 errors and warnings

* add missing field for TestUser init
2022-03-15 14:28:42 -08:00
Hayden
c8c02036a3 Chore/update alembic file format (#1056)
* capture #1043

* update file format
2022-03-15 13:11:22 -08:00
Hayden
e377df2ab0 capture #1043 (#1055) 2022-03-15 12:51:15 -08:00
Hayden
8eefa05393 Feature/database backups (#1040)
* add annotations to docs

* alchemy data dumper

* initial tests

* sourcery refactor

* db backups/restore

* potential postgres fix

* potential postgres fix

* this is terrible

* potential pg fix

* cleanup

* remove unused import

* fix comparison

* generate frontend types

* update timestamp and add directory filter

* rewrite to new admin-api

* update backup routers

* add file_token response helper

* update imports

* remove test_backup
2022-03-13 15:42:22 -08:00
Hayden
2d1ef7173d Fix/fix slug names (#1026)
* fix food seeder to use value instead of keys

* fix all recipe names being slugified

* add helper path utilities

* add fix script for database to rename foods

* add safe calling for fixes
2022-03-09 09:18:33 -09:00
Hayden
de6fd9472d Feature/collapse recipe sections (#1021)
* toggle hide recipe sections

* disable parser if food or units is defined

* make inputs clearable

* remove console.logs

* fix linter error

* fix linter errors
2022-03-03 19:43:56 -09:00
Hayden
568a1a0015 Fix/multiple bug fixes (#1015)
* test-case for #1011

* revert regressions for #1011

* update cache key on new image

* lint

* fix #1012

* typing

* random_recipe fixture

* remove delete button when no listeners are present

* spacing

* update copy to match settings value
2022-02-27 12:48:21 -09:00
Hayden
6a5fd8e4f8 Style/update mobile styles (#1008)
* update styling for mobile

* remove Coming Soon
2022-02-26 14:00:55 -09:00
Hayden
faf88ff820 add wake-lock support and toggle support (#1006)
* add wake-lock support and toggle support

* add note in change-log

* bump dependencies

* update discord webhooks
2022-02-26 11:20:49 -09:00
Hayden
1ffd668033 update pwa support + web fonts performance (#1005)
* update pwa support + web fonts performance

* update web manifest
2022-02-24 15:49:58 -09:00
Hayden
ccfaa02b03 Fix/multiple bug reports (#1002)
* fix type issues for #999

* fix regression #995

* remove error from frontend and log error #996

* cleanup darkmode on login page

* keep primary color bg
2022-02-23 15:04:45 -09:00
Hayden
6ccffce320 ignore unset when writing to db (#993)
* ignore unset when writing to db

* exception

* add 'side' types to enum set
2022-02-22 19:44:40 -09:00
Hayden
2211174636 Feature/UI updates (#990)
* titleCase utility

* update rules ui

* order by date_added

* fix error on page refresh

* fix health checks

* fix cookbook return values
2022-02-22 15:32:13 -09:00
Hayden
177a430d8c Feature/new-login-page (#989)
* login page refresh

* use user_id for token identification
2022-02-22 11:36:58 -09:00
Hayden
684f39fe24 hotfix: fix dockerfile for alembic 2022-02-21 15:03:48 -09:00
Philipp Fischbeck
fdfb5b1a5e Set up alembic migration usage (#954)
* Set up alembic migration usage

* Fix import order, add isort as alembic hook

* Only run migrations if needed

* Include date as part of migration file name for better sorting

* Skip initial migrations if tables already exist
This eases the transition from a pre-alembic state

* Fix deprecation warning in alembic revision

* Add alembic test stubs
2022-02-21 11:44:23 -09:00
Hayden
a897e180ed Bug/error relating units foods (#987)
* fix type guard

* update typing

* rearrange methods

* spelling

* fix unknown error response

* update type check for none types

* add discord notification
2022-02-21 09:47:00 -09:00
Hayden
5310a94478 remove default password and header (#984) 2022-02-20 15:14:31 -09:00
Hayden
602f248541 feature/multi-tenancy and move caddy server (#980)
* update to GUIDs

* fix cookbook id relationships

* update webhook keys

* cleanup naming and attribute orders

* remove old database tables

* fix meal-plan images

* remove dashbaord and events api

* use recipe-id instead of id

* cleanup documentation assets

* cleanup docs for v1 beta-release

* add depends_on for docker-compose

* use docker volumes for examples

* move caddy to frontend container
2022-02-20 14:17:51 -09:00
Hayden
14cc541f7a fix #967 and test error to catch (#979)
* fix #967 and test error to catch

* add admin tests
2022-02-13 18:33:25 -09:00
Hayden
c617251f4c feature: proper multi-tenant-support (#969)(WIP)
* update naming

* refactor tests to use shared structure

* shorten names

* add tools test case

* refactor to support multi-tenant

* set group_id on creation

* initial refactor for multitenant tags/cats

* spelling

* additional test case for same valued resources

* fix recipe update tests

* apply indexes to foreign keys

* fix performance regressions

* handle unknown exception

* utility decorator for function debugging

* migrate recipe_id to UUID

* GUID for recipes

* remove unused import

* move image functions into package

* move utilities to packages dir

* update import

* linter

* image image and asset routes

* update assets and images to use UUIDs

* fix migration base

* image asset test coverage

* use ids for categories and tag crud functions

* refactor recipe organizer test suite to reduce duplication

* add uuid serlization utility

* organizer base router

* slug routes testing and fixes

* fix postgres error

* adopt UUIDs

* move tags, categories, and tools under "organizers" umbrella

* update composite label

* generate ts types

* fix import error

* update frontend types

* fix type errors

* fix postgres errors

* fix #978

* add null check for title validation

* add note in docs on multi-tenancy
2022-02-13 12:23:42 -09:00
Hayden
9a82a172cb update foods and units for multitenant support 2022-02-08 14:55:18 -09:00
Hayden
fbc17b670d update env settings for production and testing 2022-02-08 13:38:12 -09:00
Hayden
ac07065613 add sponsor to readme 2022-02-08 09:25:15 -09:00
Hayden
868f55eeca set TESTING=false in containers (#968) 2022-02-07 21:25:28 -09:00
Hayden
d1024e272d Feature/automated meal planner (#939)
* cleanup oversized buttons

* add get all by category function to reciep repos

* fix shopping-list can_merge logic

* use randomized data for testing

* add random getter to repository for meal-planner

* add stub route for random meals

* cleanup global namespace

* add rules database type

* fix type

* add plan rules schema

* test plan rules methods

* add mealplan rules controller

* add new repository

* update frontend types

* formatting

* fix regression

* update autogenerated types

* add api class for mealplan rules

* add tests and fix bugs

* fix data returns

* proof of concept rules editor

* add tag support

* remove old group categories

* add tag support

* implement random by rules api

* change snack to sides

* remove incorrect typing

* split repo for custom methods

* fix query and use and_ clause

* use repo function

* remove old test

* update changelog
2022-02-07 19:03:11 -09:00
Hayden
40d1f586cd fix type reference 2022-01-16 16:15:14 -09:00
Hayden
92cf97e401 Feature/shopping lists second try (#927)
* generate types

* use generated types

* ui updates

* init button link for common styles

* add links

* setup label views

* add delete confirmation

* reset when not saved

* link label to foods and auto set when adding to shopping list

* generate types

* use inheritence to manage exception handling

* fix schema generation and add test for open_api generation

* add header to api docs

* move list consilidation to service

* split list and list items controller

* shopping list/list item tests - PARTIAL

* enable recipe add/remove in shopping lists

* generate types

* linting

* init global utility components

* update types and add list item api

* fix import cycle and database error

* add container and border classes

* new recipe list component

* fix tests

* breakout item editor

* refactor item editor

* update bulk actions

* update input / color contrast

* type generation

* refactor controller dependencies

* include food/unit editor

* remove console.logs

* fix and update type generation

* fix incorrect type for column

* fix postgres error

* fix delete by variable

* auto remove refs

* fix typo
2022-01-16 15:24:24 -09:00
Philipp Fischbeck
f794208862 Fix more typing issues (#928)
* Fix or comment several ts-ignores

* Fix typing related to BaseOverflowButton

* Remove unused functionality of useCookbooks, fix usage bug

* Fix more typing, add some comments

* Only allow ts-ignore if it has a comment
2022-01-15 17:38:11 -09:00
Hayden
c4540f1395 Refactor/conver to controllers (#923)
* add dependency injection for get_repositories

* convert events api to controller

* update generic typing

* add abstract controllers

* update test naming

* migrate admin services to controllers

* add additional admin route tests

* remove print

* add public shared dependencies

* add types

* fix typo

* add static variables for recipe json keys

* add coverage gutters config

* update controller routers

* add generic success response

* add category/tag/tool tests

* add token refresh test

* add coverage utilities

* covert comments to controller

* add todo

* add helper properties

* delete old service

* update test notes

* add unit test for pretty_stats

* remove dead code from post_webhooks

* update group routes to use controllers

* add additional group test coverage

* abstract common permission checks

* convert ingredient parser to controller

* update recipe crud to use controller

* remove dead-code

* add class lifespan tracker for debugging

* convert bulk export to controller

* migrate tools router to controller

* update recipe share to controller

* move customer router to _base

* ignore prints in flake8

* convert units and foods to new controllers

* migrate user routes to controllers

* centralize error handling

* fix invalid ref

* reorder fields

* update routers to share common handling

* update tests

* remove prints

* fix cookbooks delete

* fix cookbook get

* add controller for mealplanner

* cover report routes to controller

* remove __future__ imports

* remove dead code

* remove all base_http children and remove dead code
2022-01-13 13:06:52 -09:00
Hayden
5823a32daf update developer getting-started (#919)
* update developer getting-started

* update change-log

* fix missing title
2022-01-10 10:06:39 -09:00
Hayden
190773c5d7 Feature/group based notifications (#918)
* fix group page

* setup group notification for backend

* update type generators

* script to auto-generate schema exports

* setup frontend CRUD interface

* remove old notifications UI

* drop old events api

* add test functionality

* update naming for fields

* add event dispatcher functionality

* bump to python 3.10

* bump python version

* purge old event code

* use-async apprise

* set mealie logo as image

* unify styles for buttons rows

* add links to banners
2022-01-09 21:04:24 -09:00
Hayden
50a341ed3f build error hot-fix 2022-01-09 10:07:25 -09:00
Hayden
6db1357064 feat: (WIP) base-shoppinglist infra (#911)
* feat:  base-shoppinglist infra (WIP)

* add type checker

* implement controllers

* apply router fixes

* add checked section hide/animation

* add label support

* formatting

* fix overflow images

* add experimental banner

* fix #912 word break issue

* remove any type errors

* bump dependencies

* remove templates

* fix build errors

* bump node version

* fix template literal
2022-01-08 22:24:34 -09:00
Philipp Fischbeck
86c99b10a2 Use composition API for more components, enable more type checking (#914)
* Activate more linting rules from eslint and typescript

* Properly add VForm as type information

* Fix usage of native types

* Fix more linting issues

* Rename vuetify types file, add VTooltip

* Fix some more typing problems

* Use composition API for more components

* Convert RecipeRating

* Convert RecipeNutrition

* Convert more components to composition API

* Fix globals plugin for type checking

* Add missing icon types

* Fix vuetify types in Nuxt context

* Use composition API for RecipeActionMenu

* Convert error.vue to composition API

* Convert RecipeContextMenu to composition API

* Use more composition API and type checking in recipe/create

* Convert AppButtonUpload to composition API

* Fix some type checking in RecipeContextMenu

* Remove unused components BaseAutoForm and BaseColorPicker

* Convert RecipeCategoryTagDialog to composition API

* Convert RecipeCardSection to composition API

* Convert RecipeCategoryTagSelector to composition API

* Properly import vuetify type definitions

* Convert BaseButton to composition API

* Convert AutoForm to composition API

* Remove unused requests API file

* Remove static routes from recipe API

* Fix more type errors

* Convert AppHeader to composition API, fixing some search bar focus problems

* Convert RecipeDialogSearch to composition API

* Update API types from pydantic models, handle undefined values

* Improve more typing problems

* Add types to other plugins

* Properly type the CRUD API access

* Fix typing of static image routes

* Fix more typing stuff

* Fix some more typing problems

* Turn off more rules
2022-01-08 21:15:23 -09:00
MidnightRising
d5ab5ec66f Trims urls before importing (#910)
Co-authored-by: Midnight <midnight1@protonmail.com>
2022-01-07 12:11:19 -09:00
MidnightRising
b2b77f3abd Change the height style to min-height (#907)
Co-authored-by: Midnight <midnight1@protonmail.com>
2022-01-07 12:09:54 -09:00
Philipp Fischbeck
2c17845169 Fix double click on sidebar on smaller screens (#906)
The problem was that on smaller screens or when resizing, v-navigation-drawer would change its value on its own, but these changes were not propagated to AppSidebar.
I also added a few missing type definitions for SidebarLinks.
2022-01-07 12:09:34 -09:00
Philipp Fischbeck
76d2eecd86 Fix titles of category, tag, tool pages (#905) 2022-01-07 12:08:05 -09:00
Philipp Fischbeck
1482f51fcd Add date range to useMealplans composable (#888)
This fixes #857 by passing a date range ref to useMealplans, so that the meal plans are re-queried whenever the date range changes.
2022-01-05 12:20:57 -09:00
Hayden
74e13682cb Refactor/define repository layer (#883)
* move data access layer

* rename dal -> repo
2021-12-18 20:52:36 -09:00
Hayden
ea7c4771ee Feature/user photo storage (#877)
* add default assets for user profile

* add recipe avatar

* change user_id to UUID

* add profile image upload

* setup image cache keys

* cleanup tests and add image tests

* purge user data on delete

* new user repository tests

* add user_id validator for int -> UUID conversion

* delete depreciated route

* force set content type

* refactor tests to use temp directory

* validate parent exists before createing

* set user_id to correct type

* update instruction id

* reset primary key on migration
2021-12-18 19:04:36 -09:00
Hayden
a2f8f27193 feat: support for lockable recipes (#876)
* feat:  support for lockable recipes

* feat(backend):  check user can update before updating recipe

* test(backend):  add recipe lock tests

* feat(frontend):  disabled lock action when not owner

* test(backend):  test non-owner can't lock recipe

* hide quantity on zero value

* fix(backend): 🐛 temp/partial fix for recipes with same name. WIP
2021-12-11 19:12:08 -09:00
Hayden
ba2d9829bb frontend build hot-fix 2021-12-11 15:54:28 -09:00
Hayden
e109ac0f47 feat(backend): add rename tag, tool, category support (#875) 2021-12-10 19:48:06 -09:00
Hayden
8d77f4b31e feat: Paprika/Mealie Migration support (#873)
* feat:  paprika support - partial

* feat:  add full paprika support

* re-organize data directory

* add data directory auto-gen

* rewrite migration tests

* remove print statements

* remove hard-coded paths

* add auto-tag support

* add mealie migration support

* add looking for migraiton button
2021-12-09 19:52:53 -09:00
Hayden
5839992c19 add support for meta tags on share pages (#867) 2021-12-06 17:14:14 -09:00
Hayden
5454d2c8b8 fix sharing errors 2021-12-05 13:35:16 -09:00
Hayden
b2673d75bf Feature/shareable recipes (#866)
* simplify context menu

* move computed to comp-api

* feat:  create share tokens for recipes for sharing recieps to non-users

* feat:  shareable recipe links with og tags
2021-12-05 11:55:46 -09:00
Hayden
ba4107348f Feature/misc tasks (#864)
* feat(backend): 🌐 make foods/ingredients translatable

* feat(backend):  add remember me support for login - 14 days

* feat(frontend): 💄 add persistent darkmode for user sessions

* capture #859

* feat(frontend): 💄 add basic open-graph data for site links
2021-12-04 16:06:24 -09:00
Hayden
c32d7d7486 feat: add user recipe export functionality (#845)
* feat(frontend):  add user recipe export functionality

* remove depreciated folders

* change/remove depreciated folders

* add testing variable in config

* add GUID support for group_id

* improve testing feedback on 422 errors

* remove/cleanup files/folders

* initial user export support

* delete unused css

* update backup page UI

* remove depreciated settings

* feat:  export download links

* fix #813

* remove top level statements

* show footer

* add export purger to scheduler

* update purge glob

* fix meal-planner lockout

* feat:  add bulk delete/purge exports

* style(frontend): 💄 update UI for site settings

* feat:  add version checker

* update documentation

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-12-04 14:18:46 -09:00
Hayden
2ce195a0d4 refactor: ♻️ rewrite migrations frontend/backend (#841)
* refactor(frontend): ♻️ rewrite migrations UI

* refactor(backend): ♻️ rewrite recipe migrations

* remove vue-demi

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-11-26 22:37:06 -09:00
hay-kot
afae0ef0f5 add LDAP Deps to build file 2021-11-26 12:16:08 -09:00
hay-kot
60d5fe9277 update workflow 2021-11-26 12:08:39 -09:00
hay-kot
2e982e18ce chore(backend): ⬆️ bump recipe-scrapers and extruct 2021-11-26 12:00:02 -09:00
hay-kot
1e6adb0aad test(backend): refactor testing to reduce network reliance and speed up suite 2021-11-26 11:59:36 -09:00
hay-kot
9d9412f08e refactor(backend): ♻️ re-design scraper to utilize strategy pattern for easier testing 2021-11-26 11:58:49 -09:00
Hayden
791aa8c610 feat(backend): refactor/fix group management for admins (#838)
* fix(frontend): 🐛 update dialog implementation to simplify state management

* test(backend):  refactor test fixtures + admin group tests

* chore(backend): 🔨 add launcher.json for python debugging (tests)

* fix typing

* feat(backend):  refactor/fix group management for admins

* feat(frontend):  add/fix admin group management

* add LDAP checker

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-11-25 14:17:02 -09:00
Hayden
0db8a58963 fix(backend): 🐛 Grab PRs from dev branch (#826)
* fix(backend): 🐛 Grab PR #780

* feat(frontend):  Grab PR 797

* docs(docs): spelling

* feat(backend):  Add LDAP Support from #803

* add test deps

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-11-23 20:41:07 -09:00
Hayden
dce84c3937 refactor: ♻️ rewrite admin CRUD interface for admins (#825)
* docs: 📝 general documentation + add FAQ page

* fix(frontend): 🐛 readd missing upload button to backups.

* feat(backend):  add support for backup sizes to be displayed on frontend

* feat(backend):  add backend for administrator CRUD of users

* add admin support for user

* refactor(frontend): ♻️ rewrite admin CRUD interface for admins

* fix build errors

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-11-23 18:57:24 -09:00
Hayden
7afdd5b577 Feature/restore-recipe-functionality (#810)
* feat(frontend):  add back support for assets

* feat(backend):  add back support for assets

* feat(frontend):  add support for recipe tools

* feat(backend):  add support for recipe tools

* feat(frontend):  add onHand support for recipe toosl

* feat(backend):  add onHand support for backend

* refactor(frontend): ♻️ move items to recipe folder and break apart types

* feat(frontend):  add support for recipe comments

* feat(backend):  Add support for recipe comments

* fix(backend): 💥 disable comments import

* fix(frontend): 🐛 fix rendering issue with titles when moving steps

* add tools to changelog

* fix type errors

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-11-22 20:10:48 -09:00
Hayden
912cc6d956 feat(frontend): Add Meal Tags + UI Improvements (#807)
* feat: 

* fix colors

* add additional support for settings meal tag

* add defaults to recipe

* use group reciep settings

* fix login infinite loading

* disable owner on initial load

* add skeleton loader

* add v-model support

* formatting

* fix overwriting existing values

* feat(frontend):  add markdown preview for steps

* update black plus formatting

* update help text

* fix overwrite error

* remove print

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-11-20 14:30:38 -09:00
Hayden
d4bf81dee6 feat(frontend): food filter and add back search dialog (#794)
* return ingredients and foods on summary

* filter on foods

* update search page to TS and comp-api

* add additional search fields

* feat(frontend):  add back search dialog

* update docs

* formatting

* update sidebar - remove dropdown

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-11-08 21:12:13 -09:00
hay-kot
60275447f0 fix build errors 2021-11-06 15:03:12 -08:00
Hayden
c1ba8dcd86 feature/improve-parser-ux (#789)
* cleanup console.logs

* default to panels open

* feat(frontend):  add ingredient on enter

* feat(frontend):  automatically trigger parser on navigation

* feat(frontend):  prompt user before leaving when in editor

* add deep copy utility

* improve flow of parser

* add tooltip and match disable with disableAmount

* force admin users to have advanced access

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-11-06 14:32:55 -08:00
Hayden
788e176b16 Refactor/composables-folder (#787)
* move api clients and rename

* organize recipes composables

* rewrite useRecipeContext

* refactor(frontend): ♻️ abstract common ingredient functionality.

* feat(frontend):  add scale, and back to recipe button + hide ingredients if none

* update regex to mach 11. instead of just 1.

* minor UX improvements

Co-authored-by: Hayden K <hay-kot@pm.me>
2021-11-06 11:28:47 -08:00
Hayden
095d3bda3f feat(frontend): Rewrite context menu in TS and add 'add to mealplan' context menu action (#786)
* make entry for NLP model `setup-model`

* add comments

* feat(frontend):  Rewrite context menu in TS and add 'add to mealplan' options

* add note to changelog

Co-authored-by: Hayden K <hay-kot@pm.me>
2021-11-05 21:29:15 -08:00
Hayden
5cb4a1ade0 Feature/recipe instructions improvements (#785)
* feat(frontend):  split paragraph by 1. 1) or 1: regex matches

* feat(frontend):  Update frontend to support ingredient To step refs

* feat(backend):  Update backend to support ingredient to step refs

* fix title editor

* move about info to site-settings

* update change-log

Co-authored-by: Hayden K <hay-kot@pm.me>
2021-11-05 15:48:10 -08:00
Hayden
9f8c61a75a fix(backend): 🐛 Fix recipe page issues (#778)
* fix(backend): 🐛 Fix favorite assignment on backend

* fix(frontend): 🐛 fix printer button on recipe page

* style(frontend): 🚸 add user feadback on copy of recipe link

* fix(frontend): 🐛 Fix enableLandscape incorrect bindings to remove duplicate values

* feat(frontend):  add ingredient copy button for markdown list -[ ] format

* feat(frontend):  add remove prefix button to bulk entry

* fix(frontend): 🐛 disable random button when no recipes are present

* fix(frontend):  fix .zip download error

* fix(frontend): 🚸 close image dialog on upload/get

* fix(frontend): 🐛 fix assignment on creation for categories and tags

* feat(frontend):  Open editor on creation / fix edit button on main screen

* fix(frontend): 🐛 fix false negative regex match for urls on creationg page

* feat(frontend): 🚸 provide better user feadback when recipe exists

* feat(frontend):  lock bulk importer on submit

* remove zip from navigation

* fix(frontend):  rerender recipes on delete

Co-authored-by: Hayden K <hay-kot@pm.me>
2021-11-04 18:15:23 -08:00
Hayden
ec3b53cdc3 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): 🐛 Fix password reset bug

Co-authored-by: Hayden <hay-kot@pm.me>
2021-11-04 14:01:37 -08:00
Hayden
40462a95f1 fix(frontend): 🐛 fix section titles carrying over on deleted items (#765)
* fix(frontend): 🐛 fix section titles carrying over on deleted items

Added a UUID generator to generate unique id's and prevent list changes from causing proper virtual dom re-renders.

* lazy load json editor

* fix ingredient rendering error

* move text to input

* update settings styling

* improve mobile view

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-31 14:46:46 -08:00
Hayden
909bc85205 Chore/general UI cleanup (#764)
* unify look and feel + button validators

* Fixes #741

* add github script to mealei-next

* feat(frontend): 💄 improve user-flow for creating ingredients and units in editor

Creating a unit/food in the recipe editor will not automatically assign that to the auto-complete element on the ingredient. It also no longer needs a dialog and will show at the bottom of the menu at all times.

* fix whitespace issue with slot

* add security check to properties

* fix event refresh on delete

* remove depreciated page

* improve API token flow

* hide recipe data if not advanced user

* misc adds

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-30 15:46:44 -08:00
Hayden
2afaf70a03 feat(backend): Minor linting, bulk URL import, and improve BG tasks (#760)
* Fixes #751

* Fixes not showing original URL

* start slice at 0 instead of 1

* remove print statements

* add linter for print statements and remove print

* hide all buttons when edit disabled

* add bulk import API

* update attribute bindings

* unify button styles

* bulk add recipe feature

* thanks linter!

* uncomment code

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-28 19:28:33 -08:00
Hayden
1e5ef28f91 update database purge 2021-10-23 16:43:20 -08:00
Hayden
7055cb2c43 feat(backend): basic support for background tasks 2021-10-23 16:42:59 -08:00
Hayden
7f99c3d113 feat(frontend): Add UI for background tasks 2021-10-23 16:42:45 -08:00
Hayden
a94b9d504f refactor(frontend): ♻️ update API folder structure 2021-10-23 16:42:20 -08:00
Hayden
9b79b82c4d fix(frontend): 🐛 Fix typescript errors 2021-10-19 20:55:09 -08:00
Hayden
7e3a36f2dd feat(frontend): add debugger to creation page (#750)
* feat(frontend):  add debugger to creation page

* styling

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-19 18:45:03 -08:00
Hayden
89da1a2654 Feature/Capture Scraper Improvement PRs (#749)
* capture #732 changes

* capture PR #733

* capture PR #736

* capture pr #745

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-19 15:55:45 -08:00
Hayden
58349bc439 fix(frontend): 🚑 fix typescript errors 2021-10-19 09:33:24 -08:00
Hayden
4e70c96f8a feat: add bulk actions service and routes (WIP) (#747)
* feat(frontend):  Group level recipe data management

* feat(backend):  add bulk actions service and routes

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-18 19:41:41 -08:00
Hayden
3b920babe3 feat: Add brute strategy to ingredient processor (#744)
* fix UI column width

* words

* update parser to support diff strats

* add new model url

* make button more visible

* fix nutrition error

* feat(backend):  add 'brute' strategy for parsing ingredients

* satisfy linter

* update UI for creation page

* feat(backend):  log 422 errors in detail when not in PRODUCTION

* add strategy selector

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-16 16:06:13 -08:00
Hayden
60908e5a88 Feature/CRF++ and server side locales (#731)
* add universal toast plugin

* add server side locales

* integrate CRF++ into CI/CD Pipeline

* docs(docs): 📝 add recipe parser docs

* feat(backend):  Continued work on ingredient parsers

* add new model dest

* feat(frontend):  New ingredient parser page

* formatting

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-09 13:08:23 -08:00
Hayden
c16f07950f Docs/installation guide (#727)
* docs(docs): 📝 Update v1.0.0 installation docs

* fix frontend build

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-07 11:13:05 -08:00
Hayden
2e9026f9ea feat(frontend): Fix scheduler, forgot password flow, and minor bug fixes (#725)
* feat(frontend): 💄 add recipe title

* fix(frontend): 🐛 fixes #722 side-bar issue

* feat(frontend):  Add page titles to all pages

* minor cleanup

* refactor(backend): ♻️ rewrite scheduler to be more modulare and work

* feat(frontend):  start password reset functionality

* refactor(backend): ♻️ refactor application settings to facilitate dependency injection

* refactor(backend): 🔥 remove RECIPE_SETTINGS env variables in favor of group settings

* formatting

* refactor(backend): ♻️ align naming convention

* feat(backend):  password reset

* test(backend):  password reset

* feat(frontend):  self-service password reset

* purge password schedule

* update user creation for tests

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-07 09:39:47 -08:00
Tom Matheussen
d1f0441252 Minor typo fixes (#726) 2021-10-07 08:23:46 -08:00
Hayden
5d43fac7c9 feat(frontend): add group permissions (#721)
* style(frontend): 💄 add darktheme custom

* add dummy users in dev mode

* feat(frontend):  add group permissions editor UI

* feat(backend):  add group permissions setters

* test(backend):  tests for basic permission get/set (WIP)

Needs more testing

* remove old test

* chore(backend): copy template.env on setup

* feat(frontend):  enable send invitation via email

* feat(backend):  enable send invitation via email

* feat:  add app config checker for site-settings

* refactor(frontend): ♻️ consolidate bool checks

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-04 20:16:37 -08:00
Hayden
b7b8aa9a08 Feature/email support (#720)
* feat(frontend):  add UI for testing email configuration

* feat(backend):  add email service with common templates (WIP)

* test(backend):  add basic tests for email configuration

* set defaults

* add email variables

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-03 18:38:45 -08:00
Hayden
c0dd07f9e7 Feature/infinite scroll (#719)
* feat(frontend):  lazy-load all recipes page

* feat(frontend):  enable runtime theme through env-variables

* docs(docs): 📝 update v1 changelog

* bump version

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-03 14:07:18 -08:00
Hayden
568215cf70 perf(backend): remove validation on recipe summary response (#718)
* count responses

* perf(backend):  remove validation on recipe summary response

use the construct() method from pydantic to reduce get time as well as optimize the SQL query for recipes

* update UI to support new categories/tags

* fix(backend): 🐛 restrict recipes by group

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-02 22:07:29 -08:00
Hayden
f9829141c0 style(frontend): 🎨 refactor/rewrite UI for meal-planner (#717)
* add new creation links

* style(frontend): 🎨 refactor/rewrite UI for meal-planner

* add random meal

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-02 16:11:50 -08:00
Hayden
bd8882ec44 Feature/update login UI (#716)
* cleanup signin UI

* add timeout

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-02 12:18:01 -08:00
Hayden
4bdba9f3af feat(backend): 🚧 stub out new exporter service (WIP) (#715)
* chore(backend): 🎨 add isort path to vscode settings

* style(frontend): 💄 remove fab and add general create button

* feat(backend): 🚧 stub out new exporter service

* comment out stub tests

Co-authored-by: Hayden <hay-kot@pm.me>
2021-10-02 11:37:04 -08:00
Hayden
476aefeeb0 refactor(backend): ♻️ cleanup HTTP service classes and remove database singleton (#687)
* refactor(backend): ♻️ cleanup duplicate code in http services

* refactor(backend): ♻️ refactor database away from singleton design

removed the database single and instead injected the session into a new Database class that is created during each request life-cycle. Now sessions no longer need to be passed into each method on the database

All tests pass, but there are likely some hidden breaking changes that were not discovered.

* fix venv

* disable venv cache

* fix install script

* bump poetry version

* postgres fixes

* revert install

* fix db initialization for postgres

* add postgres to docker

* refactor(backend): ♻️ cleanup unused and duplicate code in http services

* refactor(backend): remove sessions from arguments

* refactor(backend): ♻️ convert units and ingredients to use http service class

* test(backend):  add unit and food tests

* lint

* update tags

* re-enable cache

* fix missing fraction in db

* fix lint

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-19 15:31:34 -08:00
hay-kot
c0e3f04c23 fix(frontend): 🐛 add sortablejs types 2021-09-14 12:36:46 -08:00
hay-kot
58f758b6c1 fix(frontend): 🐛 fix login page setup script 2021-09-14 12:26:28 -08:00
Hayden
b542583303 feat(backend): rewrite mealplanner with simple api (#683)
* feat(backend):  new meal-planner feature

* feat(frontend):  new meal plan feature

* refactor(backend): ♻️ refactor base services classes and add mixins for crud

* feat(frontend):  add UI/API for mealplanner

* feat(backend):  add get_today and get_slice options for mealplanner

* test(backend):  add and update group mealplanner tests

* fix(backend): 🐛 Fix recipe_id column type for PG

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-12 11:05:09 -08:00
Hayden
bdaf758712 feat(backend): start multi-tenant support (WIP) (#680)
* fix ts types

* feat(code-generation): ♻️ update code-generation formats

* new scope

* add step button

* fix linter error

* update code-generation tags

* feat(backend):  start multi-tenant support

* feat(backend):  group invitation token generation and signup

* refactor(backend): ♻️ move group admin actions to admin router

* set url base to include `/admin`

* feat(frontend):  generate user sign-up links

* test(backend):  refactor test-suite to further decouple tests (WIP)

* feat(backend): 🐛 assign owner on backup import for recipes

* fix(backend): 🐛 assign recipe owner on migration from other service

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-09 08:51:29 -08:00
Hayden
3c504e7048 feat(backend): migrate site-settings to groups (#673)
* feat(frontend):  add user registration page (WIP)

* feat(backend):  add user registration (WIP)

* test(backend):  add validator testing for registration schema

* feat(backend):  continued work on user sign-up

* feat(backend):  add signup flow and user/group settings

* test(backend):  user-creation tests and small refactor of existing tests

* fix(backend):  fix failing group tests

* style: 🎨 fix lint issues

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-05 22:05:29 -08:00
Hayden
e179dcdb10 refactor(frontend): ♻️ split user profile/management (#670)
* refactor(frontend): ♻️ major rewrite/improvement of use-profile pages

* refactor(frontend): ♻️ split webhooks into their own page

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-04 20:24:32 -08:00
Hayden
3d87ffc3a5 refactor(backend): ♻️ refactor backend services (#669)
* refactor(backend): ♻️ refactor backend services

* refactor(backend): ♻️ move user model folder into own directory for future expansion

* fix overriding results

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-04 12:28:49 -08:00
Hayden
b550dae593 refactor(backend): ♻️ change error messages to follow standard pattern to match locals on frontend (#668)
Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-02 21:33:18 -08:00
Hayden
abc0d0d59f refactor(♻️): update 'about' page to new composition API (#667)
* test-commit

* Remove PR Name Checker

* refactor(backend): ♻️ split unrelated routes into clearer router paths

Add an /app and /admin router base paths to split previously grouped public/admin data into different paths. Part of a longer migration to move 'admin' operations under the admin path.

* refactor(backend): ♻️ rename imports

* refactor(frontend): ♻️ refactor frontend API and Pages to refelect new API design

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-02 11:24:17 -08:00
hay-kot
c7f8c96287 ci(backend): 💚 add conventional pr naming check 2021-09-01 21:44:59 -08:00
Hayden
990244e37e Refactor/group page (#666)
* refactor(backend): ♻️ Refactor base class to be abstract and create a router factory method

* feat(frontend):  add group edit

* refactor(backend):  add group edit support

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-09-01 21:39:40 -08:00
Hayden
9b1bf56a5d [Feat] Migrate from Pages to Cookbooks (#664)
* feat:  Add Description to Cookbooks

* feat(frontend):  Cookbook view page

* feat(frontend): 💄 Add final UI touches

* fix(backend): 🐛 Add get by slug or id

* fix linting issue

* test(backend):  Update tests from pages -> cookbooks

* refactor(backend): 🔥 Delete old page files

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-08-31 18:51:34 -08:00
hay-kot
165fd8efd6 test(backend): add @skip for crf tests that can only run locally 2021-08-31 14:39:54 -08:00
hay-kot
d24e95c091 feat(backend): add initial cookbook support 2021-08-31 14:39:29 -08:00
hay-kot
83ab858e46 feat(frontend): Add Initial CookBook Support 2021-08-31 14:39:02 -08:00
hay-kot
2e6352cfbd update NLP for ingredients 2021-08-29 17:10:51 -08:00
hay-kot
086098899d update dependency injection methods 2021-08-29 17:10:29 -08:00
hay-kot
0b27e8af45 refactor(frontend): 💄 Imrove UI for parser 2021-08-29 17:09:08 -08:00
hay-kot
f78eb4ec39 fix(backend): 🐛 fix error when assigning favorite recipe 2021-08-28 17:42:12 -08:00
hay-kot
4a7f8428c5 refactor(backend): ♻️ rename UserInDb -> PrivateUser 2021-08-28 17:31:05 -08:00
hay-kot
df002c383c add docs string 2021-08-28 16:34:47 -08:00
hay-kot
8e9280efaf refactor(backend): ♻️ align variable names, eliminate dead-code, and finalize recipe services 2021-08-28 16:24:14 -08:00
hay-kot
985ad8017d refactor(backend): ♻️ move router dependencies to mealie.core.dependencies module 2021-08-28 15:36:46 -08:00
hay-kot
e1fb306a92 sort imports 2021-08-28 15:36:06 -08:00
hay-kot
7c936c85ae style(backend): 🎨 add isort to lint and CI/CD 2021-08-28 14:27:56 -08:00
hay-kot
2c80980453 unicode fraction processing 2021-08-28 14:18:56 -08:00
hay-kot
1c11f6a3d7 refactor(backend): ⚗️ experimental dependency injection framework (WIP) 2021-08-28 14:18:38 -08:00
hay-kot
da501adce8 refactor(backend): ♻️ add initial support for global dependency injection 2021-08-28 14:16:00 -08:00
hay-kot
0f6f81eb27 feat(frontend): 🚧 groundwork for user interactive ingredient parsing 2021-08-28 14:14:21 -08:00
hay-kot
f8c2f760bd style(backend): 🎨 2021-08-28 14:13:10 -08:00
hay-kot
20d847ec8e feat(backend): Add NLP Endpoint for Ingredient Parser (WIP) 2021-08-27 20:51:49 -08:00
hay-kot
161618808e refactor(backend): 🔥 Remove Backend Theme Support 2021-08-27 20:47:24 -08:00
hay-kot
dd1b1ad067 initialize food and units 2021-08-27 20:28:54 -08:00
hay-kot
a266a244d9 feat(backend): Add Generic Type Hint Support for Data Access Layer 2021-08-27 20:27:20 -08:00
hay-kot
0675c570ce refactor(backend): ♻️ Split Recipe Schema Code 2021-08-27 20:17:41 -08:00
hay-kot
5ba337ab11 feat(frontend): Fractional Scaling 2021-08-27 20:05:02 -08:00
hay-kot
d1a7ec3b95 fix(backend): 🐛 Fix CI/CD Linting Failures 2021-08-23 15:33:39 -08:00
hay-kot
abb0b84e22 fix auto initialize models 2021-08-23 12:25:16 -08:00
hay-kot
24a4b5b810 feat(frontend): Add Recipe Editor to Assign Units and Foods for Recipe Scaling 2021-08-23 12:24:38 -08:00
hay-kot
04ebc07333 fix restore from backup 2021-08-22 16:15:08 -08:00
hay-kot
234db39cc7 refactor(backend): ♻️ Refactor to make SessionLocal a globally accessible object 2021-08-22 16:08:37 -08:00
hay-kot
a1aad078da feat(frontend): Create CRUD User Interface for Units and Foods 2021-08-22 15:23:45 -08:00
hay-kot
122d35ec09 feat(backend): 🗃️ Add CRUD opertaions for Food and Units 2021-08-22 13:10:18 -08:00
hay-kot
c894d3d880 fix route path 2021-08-22 11:23:14 -08:00
hay-kot
62836e5381 refactor(backend): ♻️ Update tag naming and reorganized some routes. Still WIP 2021-08-21 21:49:14 -08:00
hay-kot
19fb6af050 refactor(backend): ♻️ New tag naming for group router 2021-08-21 15:13:42 -08:00
hay-kot
34a5587add fix auth tags 2021-08-21 15:08:29 -08:00
hay-kot
05f2eab1ea refactor(backend): ♻️ organize and tag user routes by path 2021-08-21 15:05:00 -08:00
hay-kot
7d40f8d74d events api 2021-08-21 14:04:49 -08:00
hay-kot
6d3a06e1b9 cleanup todos 2021-08-21 11:00:42 -08:00
hay-kot
d1b8add4e4 remove unused items 2021-08-21 00:53:08 -08:00
hay-kot
edae7bbb21 refactor(frontend): 🔥 rewrite backup UI for new page base components
Removed old split code and used the composition api to to re-write the import/export functionality of mealie.
2021-08-21 00:46:43 -08:00
hay-kot
460f508f79 whitespace 2021-08-09 17:45:09 -08:00
hay-kot
aa3b732cb0 remove old code 2021-08-09 17:37:37 -08:00
hay-kot
1338c42420 fix caddy configuration 2021-08-09 17:37:13 -08:00
hay-kot
a0efcb38a0 add traceback in development 2021-08-09 16:19:32 -08:00
hay-kot
8710dad727 fix recipe static routes 2021-08-09 16:19:23 -08:00
hay-kot
625dbcdea5 fix failing build 2021-08-08 21:15:20 -08:00
hay-kot
bde885dc84 refactor(frontend): ♻️ rewrite search componenets to typescript 2021-08-08 20:52:44 -08:00
hay-kot
1981e191be Setup vite for development 2021-08-08 17:01:45 -08:00
hay-kot
da425c970e properly support subroutes 2021-08-08 14:10:32 -08:00
hay-kot
fcee79a3e7 fix subpath config 2021-08-08 11:17:03 -08:00
hay-kot
ff2b681980 add nightly CI/CD 2021-08-07 20:22:22 -08:00
hay-kot
886d1b7a50 fix ci/cd 2021-08-07 20:21:44 -08:00
hay-kot
a2508e5bbe add stalebot config 2021-08-07 20:12:40 -08:00
hay-kot
1b555dbd65 ci(frontend): 👷 renamed ci/cd and removed old files 2021-08-07 18:15:52 -08:00
hay-kot
ea68095cd3 remove husky 2021-08-07 17:05:46 -08:00
hay-kot
51394e3d6d update github actions 2021-08-07 17:03:10 -08:00
hay-kot
b14da7efdd fix linters 2021-08-07 16:58:58 -08:00
hay-kot
3ed197a843 fix linter issues 2021-08-07 16:49:55 -08:00
hay-kot
a1b1b529a3 Fix Async Call 2021-08-07 15:22:26 -08:00
hay-kot
b166a3f843 add rich 2021-08-07 15:14:01 -08:00
hay-kot
cb85b14e01 feat(backend): Add Rich for Development Logging 2021-08-07 15:13:42 -08:00
hay-kot
9386cc320b refactor(frontend): 🚧 Migrate Dashboard to Nuxt
Add API and Functinality for Admin Dashboard. Stills needs to clean-up. See // TODO's
2021-08-07 15:12:25 -08:00
hay-kot
41a6916771 remove old files 2021-08-07 11:28:50 -08:00
hay-kot
51876508f3 improve backend performance with caching 2021-08-07 11:28:40 -08:00
hay-kot
c13fb6743c add nuxt localization 2021-08-07 11:28:20 -08:00
hay-kot
695d7e96ae refactor(frontend): 🚧 Add group/user CRUD support for admins 2021-08-06 16:28:12 -08:00
hay-kot
917177da5b test-docs ci/cd 2021-08-04 10:05:53 -08:00
hay-kot
fe41875d8b set venv settings 2021-08-04 10:00:11 -08:00
hay-kot
5a190d31dc test ci/cd 2021-08-04 09:58:51 -08:00
hay-kot
049e2f87f3 disable ci/cd 2021-08-04 09:55:15 -08:00
hay-kot
0b4cbc38e0 set to 3.8 2021-08-04 09:54:35 -08:00
hay-kot
7d6f6b1bad runtime.txt 2021-08-04 09:52:40 -08:00
hay-kot
8b3644b898 test-netlify 2021-08-04 09:51:28 -08:00
hay-kot
b65a0f5722 pip3 -> pip 2021-08-04 09:45:16 -08:00
hay-kot
e29bb2212e remove cache 2021-08-04 09:43:24 -08:00
hay-kot
84043f8235 feat(docs): 👷 add netlify.toml for docs
Add netlify.toml file for automated deployed of documentation on netlify
2021-08-04 09:41:04 -08:00
hay-kot
5ee0a57163 feat(frontend): 👷 Add image operations to recipe page
Added/Fixed image upload/get process on the recipe pages as well as some additional styling
2021-08-03 21:38:45 -08:00
hay-kot
afcad2f701 feat(frontend): 🚧 CRUD Functionality 2021-08-02 22:15:11 -08:00
hay-kot
00a8fdda41 reorganize all frontend items 2021-08-01 19:24:47 -08:00
hay-kot
d67240d449 refactor schema folders 2021-08-01 19:24:33 -08:00
hay-kot
0e8e2971d0 update development scripts 2021-08-01 19:24:05 -08:00
hay-kot
791211f787 set locales folder 2021-08-01 19:23:54 -08:00
hay-kot
f6b3285ae3 refactor(frontend): 🚧 rename recipe and meal-plan components 2021-07-31 15:07:19 -08:00
hay-kot
5047b3f5af refactor(frontend): 🚧 move static assets from .old to new
Move static assets from frontend.old to frontend
2021-07-31 14:53:02 -08:00
hay-kot
91f56b42de refactor(frontend): 🏗️ scafold pages and componenets folder
Setup the file structure for components, pages, layouts, api, and all new frontend structure
2021-07-31 14:45:28 -08:00
hay-kot
8d3db89327 nuxt init 2021-07-31 14:00:28 -08:00
hay-kot
79b3985a49 add composition api 2021-07-31 13:56:59 -08:00
hay-kot
8bfee7ae2a setup yarn 2021-07-25 13:37:18 -08:00
1378 changed files with 101872 additions and 46474 deletions

40
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,40 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.224.2/containers/python-3/.devcontainer/base.Dockerfile
# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster
ARG VARIANT="3.10-bullseye"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
RUN echo "export PROMPT_COMMAND='history -a'" >> /home/vscode/.bashrc \
&& echo "export HISTFILE=~/commandhistory/.bash_history" >> /home/vscode/.bashrc \
&& chown vscode:vscode -R /home/vscode/
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_CREATE=false
# prepend poetry and venv to path
ENV PATH="$POETRY_HOME/bin:$PATH"
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python -
# RUN poetry config virtualenvs.create false
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
curl \
build-essential \
libpq-dev \
libwebp-dev \
libsasl2-dev libldap2-dev libssl-dev \
gnupg gnupg2 gnupg1
# && pip install -U --no-cache-dir pip

View File

@@ -0,0 +1,53 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.224.2/containers/python-3
{
"name": "Python 3",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"args": {
// Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local on arm64/Apple Silicon.
"VARIANT": "3.10-bullseye",
// Options
"NODE_VERSION": "16"
}
},
"mounts": [
"source=mealie-devcontainer-workspace,target=/workspaces/mealie/frontend/node_modules,type=volume",
"source=mealie-bashhistory,target=/home/vscode/commandhistory,type=volume"
],
// Set *default* container specific settings.json values on container create.
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
3000,
9000
],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "sudo chown -R vscode:vscode /workspaces/mealie/frontend/node_modules && make setup",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode",
// "features": {
// "git": "latest"
// }
}

View File

@@ -2,7 +2,6 @@
.github
.dockerignore
.gitignore
.idea
.vscode
@@ -26,4 +25,6 @@ venv
*/dist
*/data/db
*/mealie/test
*/mealie/.temp
*/mealie/.temp
model.crfmodel

View File

@@ -1,6 +1,10 @@
[flake8]
ignore = [
extend-ignore = [
E501 # Line Length - See Black Config in pyproject.toml
E402 # Import Not at Top of File
]
exclude = _all_models.py
per-file-ignores =
__init__.py:F403,F401

54
.github/ISSUE_TEMPLATE/bug-report.yaml vendored Normal file
View File

@@ -0,0 +1,54 @@
---
name: "[v0.5.x] Bug Report"
description: "submit a bug report for the current release"
body:
- type: checkboxes
id: checks
attributes:
label: First Check
description: Please confirm and check all the following options.
options:
- label: This is not a feature request
required: true
- label: I added a very descriptive title to this issue.
required: true
- label: I used the GitHub search to find a similar issue and didn't find it.
required: true
- label: I searched the Mealie documentation, with the integrated search.
required: true
- label: I already read the docs and didn't find an answer.
required: true
- label: I have checked for existing issues that have been resolved in v1-beta
required: true
- type: textarea
id: description
attributes:
label: What is the issue you are experiencing?
placeholder: A clear and concise description of what the bug is.
validations:
required: true
- type: dropdown
id: os
attributes:
label: Deployment
description: What Deployment system are you using?
multiple: true
options:
- Docker (Linux)
- Docker (Windows)
- Docker (Synology)
- Unraid
- Other
validations:
required: true
- type: textarea
id: os-details
attributes:
label: Deployment Details
description: You can add more details about your operating system here, in particular if you chose "Other". If you are experiencing issues with deployment, please provide your docker-compose or docker commands
- type: input
id: mealie-version
attributes:
label: Mealie Version
validations:
required: true

View File

@@ -1,38 +0,0 @@
---
name: Bug Report
about: Create a bug report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**Steps To Reproduce**
Please be specific!
1. Go to '...'
2. Click on '....'
3. etc.
**Sample Code**
<!-- If applicable, please include Sample code to reproduce the issue. -->
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Actual Behavior**
<!-- A clear and concise description of what actually happens. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Device Information (please complete the following information):**
- OS: [e.g., WSL2 on Win10, Mac]
- Deployment: [e.g., Docker-version, docker-compose, Python application]
- Browser: [e.g., chrome, safari]
- Version: [e.g., 0.2.0-dev]
**Additional context**
<!-- Add any other context about the problem here. If applicable, please include why you think the bug is occurring and/or troubleshooting you have already performed. -->

View File

@@ -0,0 +1,47 @@
---
name: v1.0.0b Bug Report
description: "submit a bug report for the v1 beta"
title: "[v1.0.0b] - YOUR TITLE"
body:
- type: checkboxes
id: checks
attributes:
label: First Check
description: Please confirm and check all the following options.
options:
- label: This is not a feature request
required: true
- label: I added a very descriptive title to this issue.
required: true
- label: I used the GitHub search to find a similar issue and didn't find it.
required: true
- label: I searched the Mealie documentation, with the integrated search.
required: true
- label: I already read the docs and didn't find an answer.
required: true
- type: textarea
id: description
attributes:
label: What is the issue you are experiencing?
placeholder: A clear and concise description of what the bug is.
validations:
required: true
- type: dropdown
id: os
attributes:
label: Deployment
description: What Deployment system are you using?
multiple: true
options:
- Docker (Linux)
- Docker (Windows)
- Docker (Synology)
- Unraid
- Other
validations:
required: true
- type: textarea
id: os-details
attributes:
label: Deployment Details
description: You can add more details about your operating system here, in particular if you chose "Other". If you are experiencing issues with deployment, please provide your docker-compose or docker commands

38
.github/ISSUE_TEMPLATE/v1-task.yaml vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: v1.0.0b Task
description: "CONTRIBUTORS ONLY: Submit a Task that needs to be completed"
title: "[v1.0.0b] [Task] - TASK DESCRIPTION"
labels:
- task
- v1
body:
- type: markdown
attributes:
value: |
Thanks for your interest in Mealie! 🚀
This is a place for Mealie contributors to find tasks that need to get done around the repository. Tasks are different than issues as they are generally related to providing a new feature or improve an existing feature. They are _generally_ not related to an issue.
**DO NOT** create a task unless
- You are a contributors who has prior approval via discord/discussions
- You have otherwise been given approval to post the tasks
Otherwise, your post will be closed/deleted.
**Interested in Taking This?**
If you're interested in completing this tasks and it hasn't already been taken, comment below and to let others know you're working on it. As you work through the task, I ask that you submit a draft pull request as soon as possible, and tag this issue so we can all collaborate as best as possible.
- type: textarea
id: problem
attributes:
label: What is the problem this task addresses?
placeholder: A clear and concise description of what the problem this task will address.
validations:
required: true
- type: textarea
id: solution
attributes:
label: Proposed/Possible Solution(s)?
placeholder: Provide as much context around the idea as possible with potential files and roadblocks that may come up
validations:
required: true

32
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
version: 2
updates:
# Fetch and update latest `npm` packages
- package-ecosystem: npm
directory: "/frontend"
schedule:
interval: daily
time: "00:00"
open-pull-requests-limit: 10
reviewers:
- hay-kot
assignees:
- hay-kot
commit-message:
prefix: fix
prefix-development: chore
include: scope
# Fetch and update latest `github-actions` pkgs
- package-ecosystem: github-actions
directory: "/frontend"
schedule:
interval: daily
time: "00:00"
open-pull-requests-limit: 10
reviewers:
- hay-kot
assignees:
- hay-kot
commit-message:
prefix: fix
prefix-development: chore
include: scope

5
.github/semantic.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
# Always validate the PR title AND all the commits
titleAndCommits: true
# Allows use of Merge commits (eg on github: "Merge branch 'master' into feature/ride-unicorns")
# this is only relevant when using commitsOnly: true (or titleAndCommits: true)
allowMergeCommits: true

21
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- early-stages
- "bug: confirmed"
- feedback
- task
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -1,22 +1,19 @@
name: Docker Build Production
name: Backend - Nightly Build
on:
release:
types: [published]
push:
branches:
- mealie-next
concurrency:
group: backend-nightly-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
#
# Get Release Version
#
- uses: oprypin/find-latest-tag@v1
with:
repository: hay-kot/mealie # The repository to scan.
releases-only: true # We know that all relevant tags have a GitHub release for them.
id: mealie_version # The step ID to refer to later.
#
# Checkout
#
- name: checkout code
@@ -51,6 +48,16 @@ jobs:
#
- name: build the image
run: |
docker build --push \
--tag hkotel/mealie:${{ steps.mealie_version.outputs.tag }} \
--platform linux/amd64,linux/arm/v7,linux/arm64 .
docker build --push --no-cache \
--tag hkotel/mealie:api-nightly \
--build-arg COMMIT=$(git rev-parse HEAD) \
--platform linux/amd64,linux/arm64 .
#
# Build Discord Notification
#
- name: Discord notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }}
uses: Ilshidur/action-discord@0.3.2
with:
args: "🚀 A New build of mealie:api-nightly is available"

View File

@@ -1,20 +1,27 @@
name: Project Tests
name: Backend Tests
on:
push:
branches:
- master
- dev
- mealie-next
pull_request:
branches:
- master
- dev
- mealie-next
types: [synchronize, opened, reopened, ready_for_review]
jobs:
tests:
runs-on: ubuntu-latest
env:
PRODUCTION: false
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
# Database ENV Variablse as Specified by Mealie
Database: [sqlite, postgres]
# Services
services:
postgres:
image: postgres
@@ -25,6 +32,7 @@ jobs:
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
ports:
- 5432:5432
# Steps
steps:
#----------------------------------------------
# check-out repo and set-up python
@@ -34,12 +42,12 @@ jobs:
- name: Set up python
uses: actions/setup-python@v2
with:
python-version: 3.9
python-version: "3.10"
#----------------------------------------------
# ----- install & configure poetry -----
#----------------------------------------------
- name: Install Poetry
uses: snok/install-poetry@v1.1.1
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
@@ -57,21 +65,28 @@ jobs:
#----------------------------------------------
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install libsasl2-dev libldap2-dev libssl-dev
poetry install
poetry add "psycopg2-binary==2.8.6"
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
#----------------------------------------------
# run test suite
#----------------------------------------------
- name: Run Test Suite
- name: Formatting (Black & isort)
run: |
make test-all
#----------------------------------------------
# run test suite
#----------------------------------------------
- name: Run Test Suite Postgres
poetry run black . --check
poetry run isort . --check-only
- name: Lint (Flake8)
run: |
make backend-lint
- name: Mypy Typecheck
run: |
make backend-typecheck
- name: Pytest
env:
DB_ENGINE: postgres
DB_ENGINE: ${{ matrix.Database }}
POSTGRES_SERVER: localhost
run: |
make test-all
make backend-test

75
.github/workflows/beta-release.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
name: Docker Build Production
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
#
# Get Release Version
#
- uses: oprypin/find-latest-tag@v1
with:
repository: hay-kot/mealie # The repository to scan.
releases-only: true # We know that all relevant tags have a GitHub release for them.
id: mealie_version # The step ID to refer to later.
#
# Checkout
#
- name: checkout code
uses: actions/checkout@v2
#
# Setup QEMU
#
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
#
# Setup Buildx
#
- name: install buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
install: true
#
# Login to Docker Hub
#
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
#
# Build Backend
#
- name: build the image
run: |
docker build --push --no-cache \
--tag hkotel/mealie:api-${{ steps.mealie_version.outputs.tag }} \
--build-arg COMMIT=$(git rev-parse HEAD) \
--platform linux/amd64,linux/arm64 .
#
# Build Frontend
#
- name: build the image
working-directory: "frontend"
run: |
docker build --push --no-cache \
--tag hkotel/mealie:frontend-${{ steps.mealie_version.outputs.tag }} \
--platform linux/amd64,linux/arm64 .
#
# Release Discord Notification
#
- name: Discord notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
uses: Ilshidur/action-discord@0.3.2
with:
args: '🚀 Version {{ EVENT_PAYLOAD.release.tag_name }} of Mealie has been released. See the release notes https://github.com/hay-kot/mealie/releases/tag/{{ EVENT_PAYLOAD.release.tag_name }}'

View File

@@ -1,20 +0,0 @@
name: Publish docs via GitHub Pages
on:
push:
branches:
- master
jobs:
build:
name: Deploy docs
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v1
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONFIG_FILE: docs/mkdocs.yml
EXTRA_PACKAGES: build-base

View File

@@ -1,30 +0,0 @@
name: Docker Build Dev Docs
on:
push:
branches:
- dev
jobs:
push_to_registry:
name: Push Docker image to GitHub Packages
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Log in to GitHub Docker Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build container image
uses: docker/build-push-action@v2
with:
context: ./docs
push: true
tags: |
ghcr.io/${{ github.repository }}/dev-docs:latest

View File

@@ -1,48 +0,0 @@
name: Docker Build Production
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
#
# Checkout
#
- name: checkout code
uses: actions/checkout@v2
#
# Setup QEMU
#
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
#
# Setup Buildx
#
- name: install buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
install: true
#
# Login to Docker Hub
#
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
#
# Build
#
- name: build the image
run: |
docker build --push \
--tag hkotel/mealie:latest \
--platform linux/amd64,linux/arm/v7,linux/arm64 .

View File

@@ -1,9 +1,13 @@
name: Docker Build Dev
name: Frontend - Nightly Build
on:
push:
branches:
- dev
- mealie-next
concurrency:
group: frontend-nightly-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
@@ -43,7 +47,17 @@ jobs:
# Build
#
- name: build the image
working-directory: "frontend"
run: |
docker build --push --no-cache \
--tag hkotel/mealie:dev \
--tag hkotel/mealie:frontend-nightly \
--platform linux/amd64,linux/arm64 .
#
# Build Discord Notification
#
- name: Discord notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }}
uses: Ilshidur/action-discord@0.3.2
with:
args: "🚀 A New build of mealie:frontend-nightly is available"

88
.github/workflows/frontend-lint.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Frontend Lint
on:
push:
branches:
- mealie-next
pull_request:
branches:
- mealie-next
jobs:
lint:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node: [16]
steps:
- name: Checkout 🛎
uses: actions/checkout@master
- name: Setup node env 🏗
uses: actions/setup-node@v2.1.5
with:
node-version: ${{ matrix.node }}
check-latest: true
- name: Get yarn cache directory path 🛠
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Cache node_modules 📦
uses: actions/cache@v2.1.4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies 👨🏻‍💻
run: yarn
working-directory: "frontend"
- name: Run linter 👀
run: yarn lint
working-directory: "frontend"
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node: [16]
steps:
- name: Checkout 🛎
uses: actions/checkout@master
- name: Setup node env 🏗
uses: actions/setup-node@v2.1.5
with:
node-version: ${{ matrix.node }}
check-latest: true
- name: Get yarn cache directory path 🛠
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Cache node_modules 📦
uses: actions/cache@v2.1.4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies 👨🏻‍💻
run: yarn
working-directory: "frontend"
- name: Run Build 🚚
run: yarn build
working-directory: "frontend"

10
.gitignore vendored
View File

@@ -3,6 +3,7 @@
*__pycache__/
*.py[cod]
*$py.class
*.temp
# frontend/.env.development
docs/site/
@@ -10,11 +11,13 @@ docs/site/
.secret
frontend/dist/
dev/code-generation/generated/*
dev/data/backups/*
dev/data/debug/*
dev/data/img/*
dev/data/migration/*
dev/data/users/*
dev/data/groups/*
.DS_Store
node_modules
@@ -149,4 +152,9 @@ dev/data/backups/dev_sample_data*.zip
!dev/data/backups/test*.zip
dev/data/recipes/*
dev/scripts/output/app_routes.py
dev/scripts/output/javascriptAPI/*
dev/scripts/output/javascriptAPI/*
mealie/services/scraper/ingredient_nlp/model.crfmodel
dev/code-generation/generated/openapi.json
dev/code-generation/generated/test_routes.py
mealie/services/parser_services/crfpp/model.crfmodel
lcov.info

30
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,30 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
exclude: "mkdocs.yml"
- id: check-json
exclude: (.vscode|.devcontainer)
- id: check-toml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/sondrelg/pep585-upgrade
rev: "v1.0.1" # Use the sha / tag you want to point at
hooks:
- id: upgrade-type-hints
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
- id: isort
name: isort (python)
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: "4.0.1"
hooks:
- id: flake8
additional_dependencies:
- "flake8-print==4.0.0"

View File

@@ -476,7 +476,6 @@ ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
w54
# Ignore imports when computing similarities.
ignore-imports=no

25
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,25 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Mealie Backend",
"type": "python",
"request": "launch",
"module": "uvicorn",
"args": [
"mealie.app:app",
"--port",
"9000"
],
"env": {
"PRODUCTION": "False",
},
"justMyCode": false,
"jinja": true,
"preLaunchTask": "Init Database"
}
]
}

65
.vscode/settings.json vendored
View File

@@ -1,19 +1,58 @@
{
"python.formatting.provider": "black",
"python.pythonPath": ".venv/bin/python3.9",
"python.linting.pylintEnabled": false,
"python.linting.enabled": true,
"python.testing.unittestEnabled": false,
"python.testing.nosetestsEnabled": false,
"python.testing.pytestEnabled": true,
"python.testing.autoTestDiscoverOnSaveEnabled": false,
"python.testing.pytestArgs": ["tests"],
"conventionalCommits.scopes": [
"frontend",
"docs",
"backend",
"code-generation"
],
"cSpell.enableFiletypes": ["!javascript", "!python", "!yaml"],
"i18n-ally.localesPaths": "frontend/src/locales/messages",
"i18n-ally.sourceLanguage": "en-US",
"cSpell.words": [
"chowdown",
"compression",
"hkotel",
"nextcloud",
"performant",
"postgres",
"webp"
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": false
},
"editor.formatOnSave": true,
"eslint.workingDirectories": ["./frontend"],
"files.exclude": {
"**/__pycache__": true,
"**/.DS_Store": true,
"**/.git": true,
"**/.hg": true,
"**/.svn": true,
"**/CVS": true
},
"i18n-ally.enabledFrameworks": ["vue"],
"i18n-ally.keystyle": "nested",
"cSpell.words": ["compression", "hkotel", "performant", "postgres", "webp"],
"i18n-ally.localesPaths": "frontend/lang/messages",
"i18n-ally.sourceLanguage": "en-US",
"python.formatting.provider": "black",
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.linting.pylintEnabled": false,
"python.linting.pylintArgs": ["--rcfile=${workspaceFolder}/.pylintrc"],
"python.testing.autoTestDiscoverOnSaveEnabled": false,
"python.testing.pytestArgs": ["tests"],
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.analysis.typeCheckingMode": "off",
"python.linting.mypyEnabled": true,
"python.sortImports.path": "${workspaceFolder}/.venv/bin/isort",
"search.mode": "reuseEditor",
"python.linting.flake8Enabled": true
"python.testing.unittestArgs": ["-v", "-s", "./tests", "-p", "test_*.py"],
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"package.json": "package-lock.json, yarn.lock, .eslintrc.js, tsconfig.json, .prettierrc, .editorconfig",
"pyproject.toml": "poetry.lock, alembic.ini, .pylintrc, .flake8",
"netlify.toml": "runtime.txt",
"docker-compose.yml": "Dockerfile, .dockerignore, docker-compose.dev.yml, docker-compose.yml",
"README.md": "LICENSE, SECURITY.md"
}
}

18
.vscode/tasks.json vendored
View File

@@ -6,7 +6,9 @@
"command": "make docker-dev",
"type": "shell",
"args": [],
"problemMatcher": ["$tsc"],
"problemMatcher": [
"$tsc"
],
"presentation": {
"reveal": "always"
},
@@ -17,7 +19,9 @@
"command": "make docker-prod",
"type": "shell",
"args": [],
"problemMatcher": ["$tsc"],
"problemMatcher": [
"$tsc"
],
"presentation": {
"reveal": "always"
},
@@ -33,6 +37,16 @@
},
"problemMatcher": []
},
{
"label": "Init Database",
"command": "poetry run python mealie/db/init_db.py",
"type": "shell",
"presentation": {
"reveal": "always",
"group": "groupA"
},
"problemMatcher": []
},
{
"label": "Dev: Start Frontend",
"command": "make frontend",

View File

@@ -1,17 +1,7 @@
###############################################
# Frontend Builder Image
###############################################
FROM node:lts-alpine as frontend-build
WORKDIR /app
COPY ./frontend/package*.json ./
RUN npm install
COPY ./frontend/ .
RUN npm run build
###############################################
# Base Image
###############################################
FROM python:3.9-slim as python-base
FROM python:3.10-slim as python-base
ENV MEALIE_HOME="/app"
@@ -44,15 +34,9 @@ RUN apt-get update \
build-essential \
libpq-dev \
libwebp-dev \
# LDAP Dependencies
libsasl2-dev libldap2-dev libssl-dev \
gnupg gnupg2 gnupg1 \
debian-keyring \
debian-archive-keyring \
apt-transport-https \
&& curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | apt-key add - \
&& curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list \
&& apt-get update \
&& apt-get install --no-install-recommends -y \
caddy \
&& pip install -U --no-cache-dir pip
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
@@ -71,6 +55,7 @@ RUN poetry install -E pgsql --no-dev
###############################################
FROM python-base as development
ENV PRODUCTION=false
ENV TESTING=false
# copying poetry and venv into image
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
@@ -80,8 +65,9 @@ COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
COPY ./mealie $MEALIE_HOME/mealie
COPY ./poetry.lock ./pyproject.toml $MEALIE_HOME/
#! Future
# COPY ./alembic ./alembic.ini $MEALIE_HOME/
# Alembic
COPY ./alembic $MEALIE_HOME/alembic
COPY ./alembic.ini $MEALIE_HOME/
# venv already has runtime deps installed we get a quicker install
WORKDIR $MEALIE_HOME
@@ -91,11 +77,22 @@ WORKDIR /
RUN chmod +x $MEALIE_HOME/mealie/run.sh
ENTRYPOINT $MEALIE_HOME/mealie/run.sh "reload"
###############################################
# CRFPP Image
###############################################
FROM hkotel/crfpp as crfpp
RUN echo "crfpp-container"
###############################################
# Production Image
###############################################
FROM python-base as production
ENV PRODUCTION=true
ENV TESTING=false
ARG COMMIT
ENV GIT_COMMIT_HASH=$COMMIT
# curl for used by healthcheck
RUN apt-get update \
@@ -108,33 +105,37 @@ RUN apt-get update \
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
# copying caddy into image
COPY --from=builder-base /usr/bin/caddy /usr/bin/caddy
# copy CRF++ Binary from crfpp
ENV CRF_MODEL_URL=https://github.com/mealie-recipes/nlp-model/releases/download/v1.0.0/model.crfmodel
ENV LD_LIBRARY_PATH=/usr/local/lib
COPY --from=crfpp /usr/local/lib/ /usr/local/lib
COPY --from=crfpp /usr/local/bin/crf_learn /usr/local/bin/crf_learn
COPY --from=crfpp /usr/local/bin/crf_test /usr/local/bin/crf_test
# copy backend
COPY ./mealie $MEALIE_HOME/mealie
COPY ./poetry.lock ./pyproject.toml $MEALIE_HOME/
COPY ./gunicorn_conf.py $MEALIE_HOME
#! Future
# COPY ./alembic ./alembic.ini $MEALIE_HOME/
# Alembic
COPY ./alembic $MEALIE_HOME/alembic
COPY ./alembic.ini $MEALIE_HOME/
# venv already has runtime deps installed we get a quicker install
WORKDIR $MEALIE_HOME
RUN . $VENV_PATH/bin/activate && poetry install -E pgsql --no-dev
WORKDIR /
# copy frontend
COPY --from=frontend-build /app/dist $MEALIE_HOME/dist
COPY ./dev/data/templates $MEALIE_HOME/data/templates
COPY ./Caddyfile $MEALIE_HOME
# Grab CRF++ Model Release
RUN curl -L0 $CRF_MODEL_URL --output $MEALIE_HOME/mealie/services/parser_services/crfpp/model.crfmodel
VOLUME [ "$MEALIE_HOME/data/" ]
ENV APP_PORT=80
ENV APP_PORT=9000
EXPOSE ${APP_PORT}
HEALTHCHECK CMD curl -f http://localhost:${APP_PORT} || exit 1
HEALTHCHECK CMD curl -f http://localhost:${APP_PORT}/docs || exit 1
RUN chmod +x $MEALIE_HOME/mealie/run.sh
ENTRYPOINT $MEALIE_HOME/mealie/run.sh

674
LICENSE
View File

@@ -1,21 +1,661 @@
MIT License
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (c) 2020, Hayden Kotelman
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

View File

@@ -5,10 +5,6 @@
[![MIT License][license-shield]][license-url]
[![Docker Pulls][docker-pull]][docker-pull]
[![CodeFactor](https://www.codefactor.io/repository/github/hay-kot/mealie/badge)](https://www.codefactor.io/repository/github/hay-kot/mealie)
[![Docker Build Production](https://github.com/hay-kot/mealie/actions/workflows/dockerbuild.release.yml/badge.svg)](https://github.com/hay-kot/mealie/actions/workflows/dockerbuild.release.yml)
[![Project Tests Production](https://github.com/hay-kot/mealie/actions/workflows/test-all.yml/badge.svg)](https://github.com/hay-kot/mealie/actions/workflows/test-all.yml)
[![Docker Build Dev](https://github.com/hay-kot/mealie/actions/workflows/dockerbuild.dev.yml/badge.svg?branch=dev)](https://github.com/hay-kot/mealie/actions/workflows/dockerbuild.dev.yml)
[![Project Tests Dev](https://github.com/hay-kot/mealie/actions/workflows/test-all.yml/badge.svg?branch=dev)](https://github.com/hay-kot/mealie/actions/workflows/test-all.yml)
<!-- PROJECT LOGO -->
<br />
@@ -24,19 +20,13 @@
<p align="center">
A Place for All Your Recipes
<br />
<a href="https://hay-kot.github.io/mealie/"><strong>Explore the docs »</strong></a>
<a href="https://nightly.mealie.io"><strong>Explore the docs »</strong></a>
<a href="https://github.com/hay-kot/mealie">
</a>
<br />
<a href="https://mealie-demo.hay-kot.dev/">View Demo</a>
<a href="https://demo.mealie.io/">View Demo</a>
·
<a href="https://github.com/hay-kot/mealie/issues">Report Bug</a>
·
<a href="https://hay-kot.github.io/mealie/api/redoc/">API</a>
·
<a href="https://github.com/hay-kot/mealie/issues">
Request Feature
</a>
<a href="https://github.com/hay-kot/mealie/issues">Report Bug</a>
·
<a href="https://hub.docker.com/r/hkotel/mealie"> Docker Hub
</a>
@@ -45,60 +35,25 @@
[![Product Name Screen Shot][product-screenshot]](https://example.com)
[![Product Name Screen Shot][product-screenshot]](https://docs.mealie.io)
# About The Project
Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and Mealie will automatically import the relevant data or add a family recipe with the UI editor. Mealie also provides an API for interactions from 3rd party applications.
Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and Mealie will automatically import the relevant data or add a family recipe with the UI editor. Mealie also provides an API for interactions from 3rd party applications.
[Remember to join the Discord](https://discord.gg/QuStdQGSGK)!
## Key Features
- 🔍 Fuzzy search
- 🏷️ Tag recipes with categories or tags for flexible sorting
- 🕸 Import recipes from around the web by URL
- 💪 Powerful bulk Category/Tag assignment
- 📱 Beautiful Mobile Views
- 📆 Create Meal Plans
- 🛒 Generate shopping lists
- 🐳 Easy setup with Docker
- 🎨 Customize your interface with color themes
- 💾 Export all your data in any format with Jinja2 Templates
- 🔒 Keep your data safe with automated backup and easy restore options
- 🌍 localized in many languages
- Plus tons more!
- Flexible API
- Custom key/value pairs for recipes
- Webhook support
- Interactive API Documentation thanks to [FastAPI](https://fastapi.tiangolo.com/) and [Swagger](https://petstore.swagger.io/)
- Raw JSON Recipe Editor
- Migration from other platforms
- Chowdown
- Nextcloud Cookbook
- Random meal plan generation
## FAQ
### Why An API?
An API allows integration into applications like [Home Assistant](https://www.home-assistant.io/) that can act as notification engines to provide custom notifications based of Meal Plan data to remind you to defrost the chicken, marinade the steak, or start the CrockPot. Additionally, you can access nearly any backend service via the API giving you total control to extend the application. To explore the API spin up your server and navigate to http://yourserver.com/docs for interactive API documentation.
### Why a Database?
Some users of static-site generator applications like ChowDown have expressed concerns about their data being stuck in a database. Considering this is a new project it is a valid concern to be worried about your data. Mealie specifically addresses this concern by provided automatic daily backups that export your data in json, plain-text markdown files, and/or custom Jinja2 templates. **This puts you in controls of how your data is represented** when exported from Mealie, which means you can easily migrate to any other service provided Mealie doesn't work for you.
As to why we need a database?
- **Developer Experience:** Without a database a lot of the work to maintain your data is taken on by the developer instead of a battle tested platform for storing data.
- **Multi User Support:** With a solid database as backend storage for your data Mealie can better support multi-user sites and avoid read/write access errors when multiple actions are taken at the same time.
- [Remember to join the Discord](https://discord.gg/QuStdQGSGK)!
- [Documentation](https://nightly.mealie.io)
<!-- CONTRIBUTING -->
## Contributing
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. Especially test. Literally any tests. See the [Contributors Guide](https://hay-kot.github.io/mealie/contributors/non-coders/) for help getting started.
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. If you're going to be working on the code-base you'll want to use the nightly documentation to ensure you get the latest information.
If you are not a coder, you can still contribute financially. financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for project development.
- See the [Contributors Guide](https://nightly.mealie.io/contributors/developers-guide/code-contributions/) for help getting started.
- We use [VSCode Dev Containers](https://code.visualstudio.com/docs/remote/containers) to make it easy for contributors to get started!
If you are not a coder, you can still contribute financially. financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for project development.
<a href="https://www.buymeacoffee.com/haykot" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 107px !important;" ></a>
@@ -107,9 +62,16 @@ If you are not a coder, you can still contribute financially. financial contribu
Distributed under the MIT License. See `LICENSE` for more information.
<!-- CONTACT -->
## Contact
Project Link: [https://github.com/hay-kot/mealie](https://github.com/hay-kot/mealie)
## Sponsors
Huge thanks to all the sponsors of this project on [Github Sponsors](https://github.com/sponsors/hay-kot) and Buy Me a Coffee. Without you this project would surely not be possible.
Thanks to Linode for providing Hosting for the Demo, Beta, and Documentation sites! Another big thanks to JetBrains for providing their IDEs for development.
<div align='center'>
<img height="100" src="docs/docs/assets/img/sponsors-linode.svg" />
<img height="100" src="docs/docs/assets/img/sponsors-jetbrains.png" />
</div>
@@ -125,7 +87,7 @@ Project Link: [https://github.com/hay-kot/mealie](https://github.com/hay-kot/mea
[issues-shield]: https://img.shields.io/github/issues/hay-kot/mealie.svg?style=flat-square
[issues-url]: https://github.com/hay-kot/mealie/issues
[license-shield]: https://img.shields.io/github/license/hay-kot/mealie.svg?style=flat-square
[license-url]: https://github.com/hay-kot/mealie/blob/master/LICENSE.txt
[license-url]: https://github.com/hay-kot/mealie/blob/mealie-next/LICENSE
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555
[linkedin-url]: https://linkedin.com/in/hay-kot
[product-screenshot]: docs/docs/assets/img/home_screenshot.png

9
SECURITY.md Normal file
View File

@@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
Since this software is still considered beta/WIP support is always only given for the latest version. Security patches are only available for the latest version and not back-ported to older versions.
## Reporting a Vulnerability
For general security vulnerabilities you're welcome to open a GitHub issues or contribute a fix. If you feel the vulnerability should not be disclosed you can open a generic issue on GitHub and email to the details to [ob92oy0sl@mozmail.com](mailto:ob92oy0sl@mozmail.com) which is monitored by the maintainer.

72
alembic.ini Normal file
View File

@@ -0,0 +1,72 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration files
file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d.%%(minute).2d.%%(second).2d_%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python-dateutil library that can be
# installed by adding `alembic[tz]` to the pip requirements
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator"
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
# version path separator; As mentioned above, this is the character used to split
# version_locations. Valid values are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
version_path_separator = os # default: use os.pathsep
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
# Will be replaced in alembic/env.py with the actual url
sqlalchemy.url =
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
hooks = isort, black
# format using "isort" - use the console_scripts runner, against the "isort" entrypoint
isort.type = console_scripts
isort.entrypoint = isort
isort.options = REVISION_SCRIPT_FILENAME
# format using "black" - use the console_scripts runner, against the "black" entrypoint
black.type = console_scripts
black.entrypoint = black
black.options = REVISION_SCRIPT_FILENAME

1
alembic/README Normal file
View File

@@ -0,0 +1 @@
Generic single-database configuration.

77
alembic/env.py Normal file
View File

@@ -0,0 +1,77 @@
from sqlalchemy import engine_from_config, pool
import mealie.db.models._all_models # noqa: F401
from alembic import context
from mealie.core.config import get_app_settings
from mealie.db.models._model_base import SqlAlchemyBase
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = SqlAlchemyBase.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
# Set DB url from config
settings = get_app_settings()
config.set_main_option("sqlalchemy.url", settings.DB_URL)
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata, user_module_prefix="mealie.db.migration_types."
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

26
alembic/script.py.mako Normal file
View File

@@ -0,0 +1,26 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
import sqlalchemy as sa
import mealie.db.migration_types
from alembic import op
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -0,0 +1,920 @@
"""Initial tables
Revision ID: 6b0f5f32d602
Revises:
Create Date: 2022-02-21 19:56:24.351115
"""
import sqlalchemy as sa
from sqlalchemy import engine_from_config
import mealie.db.migration_types
from alembic import op
# revision identifiers, used by Alembic.
revision = "6b0f5f32d602"
down_revision = None
branch_labels = None
depends_on = None
# Adapted from https://improveandrepeat.com/2021/09/python-friday-87-handling-pre-existing-tables-with-alembic-and-sqlalchemy/
def table_exists(table, schema=None):
config = op.get_context().config
engine = engine_from_config(config.get_section(config.config_ini_section), prefix="sqlalchemy.")
insp = sa.inspect(engine)
return insp.has_table(table, schema)
def upgrade():
# Only create initial tables if they don't exist yet, to ease transition from pre-alembic state
if table_exists("users"):
return
op.create_table(
"groups",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_groups_name"), "groups", ["name"], unique=True)
op.create_table(
"categories",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("slug", sa.String(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("slug", "group_id", name="category_slug_group_id_key"),
)
op.create_index(op.f("ix_categories_group_id"), "categories", ["group_id"], unique=False)
op.create_index(op.f("ix_categories_name"), "categories", ["name"], unique=False)
op.create_index(op.f("ix_categories_slug"), "categories", ["slug"], unique=False)
op.create_table(
"cookbooks",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("position", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("slug", sa.String(), nullable=False),
sa.Column("description", sa.String(), nullable=True),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"group_data_exports",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("name", sa.String(), nullable=False),
sa.Column("filename", sa.String(), nullable=False),
sa.Column("path", sa.String(), nullable=False),
sa.Column("size", sa.String(), nullable=False),
sa.Column("expires", sa.String(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_group_data_exports_group_id"), "group_data_exports", ["group_id"], unique=False)
op.create_table(
"group_events_notifiers",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("enabled", sa.Boolean(), nullable=False),
sa.Column("apprise_url", sa.String(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_group_events_notifiers_group_id"), "group_events_notifiers", ["group_id"], unique=False)
op.create_table(
"group_meal_plan_rules",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("day", sa.String(), nullable=False),
sa.Column("entry_type", sa.String(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"group_preferences",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("private_group", sa.Boolean(), nullable=True),
sa.Column("first_day_of_week", sa.Integer(), nullable=True),
sa.Column("recipe_public", sa.Boolean(), nullable=True),
sa.Column("recipe_show_nutrition", sa.Boolean(), nullable=True),
sa.Column("recipe_show_assets", sa.Boolean(), nullable=True),
sa.Column("recipe_landscape_view", sa.Boolean(), nullable=True),
sa.Column("recipe_disable_comments", sa.Boolean(), nullable=True),
sa.Column("recipe_disable_amount", sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_group_preferences_group_id"), "group_preferences", ["group_id"], unique=False)
op.create_table(
"group_reports",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("status", sa.String(), nullable=False),
sa.Column("category", sa.String(), nullable=False),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_group_reports_category"), "group_reports", ["category"], unique=False)
op.create_index(op.f("ix_group_reports_group_id"), "group_reports", ["group_id"], unique=False)
op.create_table(
"ingredient_units",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=True),
sa.Column("description", sa.String(), nullable=True),
sa.Column("abbreviation", sa.String(), nullable=True),
sa.Column("fraction", sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"invite_tokens",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("token", sa.String(), nullable=False),
sa.Column("uses_left", sa.Integer(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_invite_tokens_token"), "invite_tokens", ["token"], unique=True)
op.create_table(
"multi_purpose_labels",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(length=255), nullable=False),
sa.Column("color", sa.String(length=10), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_multi_purpose_labels_group_id"), "multi_purpose_labels", ["group_id"], unique=False)
op.create_table(
"recipes",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("slug", sa.String(), nullable=True),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("name", sa.String(), nullable=False),
sa.Column("description", sa.String(), nullable=True),
sa.Column("image", sa.String(), nullable=True),
sa.Column("total_time", sa.String(), nullable=True),
sa.Column("prep_time", sa.String(), nullable=True),
sa.Column("perform_time", sa.String(), nullable=True),
sa.Column("cook_time", sa.String(), nullable=True),
sa.Column("recipe_yield", sa.String(), nullable=True),
sa.Column("recipeCuisine", sa.String(), nullable=True),
sa.Column("rating", sa.Integer(), nullable=True),
sa.Column("org_url", sa.String(), nullable=True),
sa.Column("date_added", sa.Date(), nullable=True),
sa.Column("date_updated", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], use_alter=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("slug", "group_id", name="recipe_slug_group_id_key"),
)
op.create_index(op.f("ix_recipes_group_id"), "recipes", ["group_id"], unique=False)
op.create_index(op.f("ix_recipes_slug"), "recipes", ["slug"], unique=False)
op.create_index(op.f("ix_recipes_user_id"), "recipes", ["user_id"], unique=False)
op.create_table(
"server_tasks",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("name", sa.String(), nullable=False),
sa.Column("completed_date", sa.DateTime(), nullable=True),
sa.Column("status", sa.String(), nullable=False),
sa.Column("log", sa.String(), nullable=True),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_server_tasks_group_id"), "server_tasks", ["group_id"], unique=False)
op.create_table(
"shopping_lists",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_shopping_lists_group_id"), "shopping_lists", ["group_id"], unique=False)
op.create_table(
"tags",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("slug", sa.String(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("slug", "group_id", name="tags_slug_group_id_key"),
)
op.create_index(op.f("ix_tags_group_id"), "tags", ["group_id"], unique=False)
op.create_index(op.f("ix_tags_name"), "tags", ["name"], unique=False)
op.create_index(op.f("ix_tags_slug"), "tags", ["slug"], unique=False)
op.create_table(
"tools",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("slug", sa.String(), nullable=False),
sa.Column("on_hand", sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("slug", "group_id", name="tools_slug_group_id_key"),
)
op.create_index(op.f("ix_tools_name"), "tools", ["name"], unique=True)
op.create_index(op.f("ix_tools_slug"), "tools", ["slug"], unique=True)
op.create_table(
"webhook_urls",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("enabled", sa.Boolean(), nullable=True),
sa.Column("name", sa.String(), nullable=True),
sa.Column("url", sa.String(), nullable=True),
sa.Column("time", sa.String(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_webhook_urls_group_id"), "webhook_urls", ["group_id"], unique=False)
op.create_table(
"api_extras",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("recipee_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("key_name", sa.String(), nullable=True),
sa.Column("value", sa.String(), nullable=True),
sa.ForeignKeyConstraint(
["recipee_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"cookbooks_to_categories",
sa.Column("cookbook_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["category_id"],
["categories.id"],
),
sa.ForeignKeyConstraint(
["cookbook_id"],
["cookbooks.id"],
),
)
op.create_table(
"group_events_notifier_options",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("event_notifier_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("recipe_created", sa.Boolean(), nullable=False),
sa.Column("recipe_updated", sa.Boolean(), nullable=False),
sa.Column("recipe_deleted", sa.Boolean(), nullable=False),
sa.Column("user_signup", sa.Boolean(), nullable=False),
sa.Column("data_migrations", sa.Boolean(), nullable=False),
sa.Column("data_export", sa.Boolean(), nullable=False),
sa.Column("data_import", sa.Boolean(), nullable=False),
sa.Column("mealplan_entry_created", sa.Boolean(), nullable=False),
sa.Column("shopping_list_created", sa.Boolean(), nullable=False),
sa.Column("shopping_list_updated", sa.Boolean(), nullable=False),
sa.Column("shopping_list_deleted", sa.Boolean(), nullable=False),
sa.Column("cookbook_created", sa.Boolean(), nullable=False),
sa.Column("cookbook_updated", sa.Boolean(), nullable=False),
sa.Column("cookbook_deleted", sa.Boolean(), nullable=False),
sa.Column("tag_created", sa.Boolean(), nullable=False),
sa.Column("tag_updated", sa.Boolean(), nullable=False),
sa.Column("tag_deleted", sa.Boolean(), nullable=False),
sa.Column("category_created", sa.Boolean(), nullable=False),
sa.Column("category_updated", sa.Boolean(), nullable=False),
sa.Column("category_deleted", sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(
["event_notifier_id"],
["group_events_notifiers.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"group_meal_plans",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("date", sa.Date(), nullable=False),
sa.Column("entry_type", sa.String(), nullable=False),
sa.Column("title", sa.String(), nullable=False),
sa.Column("text", sa.String(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_group_meal_plans_date"), "group_meal_plans", ["date"], unique=False)
op.create_index(op.f("ix_group_meal_plans_entry_type"), "group_meal_plans", ["entry_type"], unique=False)
op.create_index(op.f("ix_group_meal_plans_group_id"), "group_meal_plans", ["group_id"], unique=False)
op.create_index(op.f("ix_group_meal_plans_recipe_id"), "group_meal_plans", ["recipe_id"], unique=False)
op.create_index(op.f("ix_group_meal_plans_title"), "group_meal_plans", ["title"], unique=False)
op.create_table(
"group_to_categories",
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["category_id"],
["categories.id"],
),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
)
op.create_table(
"ingredient_foods",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("name", sa.String(), nullable=True),
sa.Column("description", sa.String(), nullable=True),
sa.Column("label_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.ForeignKeyConstraint(
["label_id"],
["multi_purpose_labels.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"notes",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("title", sa.String(), nullable=True),
sa.Column("text", sa.String(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"plan_rules_to_categories",
sa.Column("group_plan_rule_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["category_id"],
["categories.id"],
),
sa.ForeignKeyConstraint(
["group_plan_rule_id"],
["group_meal_plan_rules.id"],
),
)
op.create_table(
"plan_rules_to_tags",
sa.Column("plan_rule_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("tag_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["plan_rule_id"],
["group_meal_plan_rules.id"],
),
sa.ForeignKeyConstraint(
["tag_id"],
["tags.id"],
),
)
op.create_table(
"recipe_assets",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("name", sa.String(), nullable=True),
sa.Column("icon", sa.String(), nullable=True),
sa.Column("file_name", sa.String(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"recipe_instructions",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("position", sa.Integer(), nullable=True),
sa.Column("type", sa.String(), nullable=True),
sa.Column("title", sa.String(), nullable=True),
sa.Column("text", sa.String(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"recipe_nutrition",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("calories", sa.String(), nullable=True),
sa.Column("fat_content", sa.String(), nullable=True),
sa.Column("fiber_content", sa.String(), nullable=True),
sa.Column("protein_content", sa.String(), nullable=True),
sa.Column("carbohydrate_content", sa.String(), nullable=True),
sa.Column("sodium_content", sa.String(), nullable=True),
sa.Column("sugar_content", sa.String(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"recipe_settings",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("public", sa.Boolean(), nullable=True),
sa.Column("show_nutrition", sa.Boolean(), nullable=True),
sa.Column("show_assets", sa.Boolean(), nullable=True),
sa.Column("landscape_view", sa.Boolean(), nullable=True),
sa.Column("disable_amount", sa.Boolean(), nullable=True),
sa.Column("disable_comments", sa.Boolean(), nullable=True),
sa.Column("locked", sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"recipe_share_tokens",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("expires_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_recipe_share_tokens_group_id"), "recipe_share_tokens", ["group_id"], unique=False)
op.create_table(
"recipes_to_categories",
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["category_id"],
["categories.id"],
),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
)
op.create_table(
"recipes_to_tags",
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("tag_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.ForeignKeyConstraint(
["tag_id"],
["tags.id"],
),
)
op.create_table(
"recipes_to_tools",
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("tool_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.ForeignKeyConstraint(
["tool_id"],
["tools.id"],
),
)
op.create_table(
"report_entries",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("success", sa.Boolean(), nullable=True),
sa.Column("message", sa.String(), nullable=True),
sa.Column("exception", sa.String(), nullable=True),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("report_id", mealie.db.migration_types.GUID(), nullable=False),
sa.ForeignKeyConstraint(
["report_id"],
["group_reports.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"shopping_list_recipe_reference",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("recipe_quantity", sa.Float(), nullable=False),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.ForeignKeyConstraint(
["shopping_list_id"],
["shopping_lists.id"],
),
sa.PrimaryKeyConstraint("id", "shopping_list_id"),
)
op.create_index(
op.f("ix_shopping_list_recipe_reference_recipe_id"),
"shopping_list_recipe_reference",
["recipe_id"],
unique=False,
)
op.create_table(
"users",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("full_name", sa.String(), nullable=True),
sa.Column("username", sa.String(), nullable=True),
sa.Column("email", sa.String(), nullable=True),
sa.Column("password", sa.String(), nullable=True),
sa.Column("admin", sa.Boolean(), nullable=True),
sa.Column("advanced", sa.Boolean(), nullable=True),
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("cache_key", sa.String(), nullable=True),
sa.Column("can_manage", sa.Boolean(), nullable=True),
sa.Column("can_invite", sa.Boolean(), nullable=True),
sa.Column("can_organize", sa.Boolean(), nullable=True),
sa.Column("owned_recipes_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["group_id"],
["groups.id"],
),
sa.ForeignKeyConstraint(
["owned_recipes_id"],
["recipes.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
op.create_index(op.f("ix_users_full_name"), "users", ["full_name"], unique=False)
op.create_index(op.f("ix_users_group_id"), "users", ["group_id"], unique=False)
op.create_index(op.f("ix_users_username"), "users", ["username"], unique=True)
op.create_table(
"long_live_tokens",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("name", sa.String(), nullable=False),
sa.Column("token", sa.String(), nullable=False),
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"password_reset_tokens",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("token", sa.String(length=64), nullable=False),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("token"),
)
op.create_table(
"recipe_comments",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("text", sa.String(), nullable=True),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"recipe_ingredient_ref_link",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("instruction_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("reference_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["instruction_id"],
["recipe_instructions.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"recipes_ingredients",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("position", sa.Integer(), nullable=True),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("title", sa.String(), nullable=True),
sa.Column("note", sa.String(), nullable=True),
sa.Column("unit_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("food_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("quantity", sa.Integer(), nullable=True),
sa.Column("reference_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["food_id"],
["ingredient_foods.id"],
),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.ForeignKeyConstraint(
["unit_id"],
["ingredient_units.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"shopping_list_items",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("is_ingredient", sa.Boolean(), nullable=True),
sa.Column("position", sa.Integer(), nullable=False),
sa.Column("checked", sa.Boolean(), nullable=True),
sa.Column("quantity", sa.Float(), nullable=True),
sa.Column("note", sa.String(), nullable=True),
sa.Column("is_food", sa.Boolean(), nullable=True),
sa.Column("unit_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("food_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("label_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["food_id"],
["ingredient_foods.id"],
),
sa.ForeignKeyConstraint(
["label_id"],
["multi_purpose_labels.id"],
),
sa.ForeignKeyConstraint(
["shopping_list_id"],
["shopping_lists.id"],
),
sa.ForeignKeyConstraint(
["unit_id"],
["ingredient_units.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"users_to_favorites",
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
)
op.create_table(
"shopping_list_item_recipe_reference",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("shopping_list_item_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("recipe_quantity", sa.Float(), nullable=False),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.ForeignKeyConstraint(
["shopping_list_item_id"],
["shopping_list_items.id"],
),
sa.PrimaryKeyConstraint("id", "shopping_list_item_id"),
)
op.create_index(
op.f("ix_shopping_list_item_recipe_reference_recipe_id"),
"shopping_list_item_recipe_reference",
["recipe_id"],
unique=False,
)
def downgrade():
op.drop_index(
op.f("ix_shopping_list_item_recipe_reference_recipe_id"), table_name="shopping_list_item_recipe_reference"
)
op.drop_table("shopping_list_item_recipe_reference")
op.drop_table("users_to_favorites")
op.drop_table("shopping_list_items")
op.drop_table("recipes_ingredients")
op.drop_table("recipe_ingredient_ref_link")
op.drop_table("recipe_comments")
op.drop_table("password_reset_tokens")
op.drop_table("long_live_tokens")
op.drop_index(op.f("ix_users_username"), table_name="users")
op.drop_index(op.f("ix_users_group_id"), table_name="users")
op.drop_index(op.f("ix_users_full_name"), table_name="users")
op.drop_index(op.f("ix_users_email"), table_name="users")
op.drop_table("users")
op.drop_index(op.f("ix_shopping_list_recipe_reference_recipe_id"), table_name="shopping_list_recipe_reference")
op.drop_table("shopping_list_recipe_reference")
op.drop_table("report_entries")
op.drop_table("recipes_to_tools")
op.drop_table("recipes_to_tags")
op.drop_table("recipes_to_categories")
op.drop_index(op.f("ix_recipe_share_tokens_group_id"), table_name="recipe_share_tokens")
op.drop_table("recipe_share_tokens")
op.drop_table("recipe_settings")
op.drop_table("recipe_nutrition")
op.drop_table("recipe_instructions")
op.drop_table("recipe_assets")
op.drop_table("plan_rules_to_tags")
op.drop_table("plan_rules_to_categories")
op.drop_table("notes")
op.drop_table("ingredient_foods")
op.drop_table("group_to_categories")
op.drop_index(op.f("ix_group_meal_plans_title"), table_name="group_meal_plans")
op.drop_index(op.f("ix_group_meal_plans_recipe_id"), table_name="group_meal_plans")
op.drop_index(op.f("ix_group_meal_plans_group_id"), table_name="group_meal_plans")
op.drop_index(op.f("ix_group_meal_plans_entry_type"), table_name="group_meal_plans")
op.drop_index(op.f("ix_group_meal_plans_date"), table_name="group_meal_plans")
op.drop_table("group_meal_plans")
op.drop_table("group_events_notifier_options")
op.drop_table("cookbooks_to_categories")
op.drop_table("api_extras")
op.drop_index(op.f("ix_webhook_urls_group_id"), table_name="webhook_urls")
op.drop_table("webhook_urls")
op.drop_index(op.f("ix_tools_slug"), table_name="tools")
op.drop_index(op.f("ix_tools_name"), table_name="tools")
op.drop_table("tools")
op.drop_index(op.f("ix_tags_slug"), table_name="tags")
op.drop_index(op.f("ix_tags_name"), table_name="tags")
op.drop_index(op.f("ix_tags_group_id"), table_name="tags")
op.drop_table("tags")
op.drop_index(op.f("ix_shopping_lists_group_id"), table_name="shopping_lists")
op.drop_table("shopping_lists")
op.drop_index(op.f("ix_server_tasks_group_id"), table_name="server_tasks")
op.drop_table("server_tasks")
op.drop_index(op.f("ix_recipes_user_id"), table_name="recipes")
op.drop_index(op.f("ix_recipes_slug"), table_name="recipes")
op.drop_index(op.f("ix_recipes_group_id"), table_name="recipes")
op.drop_table("recipes")
op.drop_index(op.f("ix_multi_purpose_labels_group_id"), table_name="multi_purpose_labels")
op.drop_table("multi_purpose_labels")
op.drop_index(op.f("ix_invite_tokens_token"), table_name="invite_tokens")
op.drop_table("invite_tokens")
op.drop_table("ingredient_units")
op.drop_index(op.f("ix_group_reports_group_id"), table_name="group_reports")
op.drop_index(op.f("ix_group_reports_category"), table_name="group_reports")
op.drop_table("group_reports")
op.drop_index(op.f("ix_group_preferences_group_id"), table_name="group_preferences")
op.drop_table("group_preferences")
op.drop_table("group_meal_plan_rules")
op.drop_index(op.f("ix_group_events_notifiers_group_id"), table_name="group_events_notifiers")
op.drop_table("group_events_notifiers")
op.drop_index(op.f("ix_group_data_exports_group_id"), table_name="group_data_exports")
op.drop_table("group_data_exports")
op.drop_table("cookbooks")
op.drop_index(op.f("ix_categories_slug"), table_name="categories")
op.drop_index(op.f("ix_categories_name"), table_name="categories")
op.drop_index(op.f("ix_categories_group_id"), table_name="categories")
op.drop_table("categories")
op.drop_index(op.f("ix_groups_name"), table_name="groups")
op.drop_table("groups")

View File

@@ -0,0 +1,51 @@
"""convert quantity from integer to float
Revision ID: 263dd6707191
Revises: 6b0f5f32d602
Create Date: 2022-03-23 17:43:34.727829
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "263dd6707191"
down_revision = "6b0f5f32d602"
branch_labels = None
depends_on = None
def is_postgres():
return op.get_context().dialect.name == "postgresql"
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
# SQLite doesn't require migration as types are not inforced.
# Postgres Specifc Migration
if is_postgres():
op.alter_column(
"recipes_ingredients",
"quantity",
type_=sa.Float(),
existing_type=sa.Integer(),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
# SQLite doesn't require migration as types are not inforced.
# Postgres Specifc Migration
if is_postgres():
op.alter_column(
"recipes_ingredients",
"quantity",
type_=sa.Integer(),
existing_type=sa.Float(),
)
# ### end Alembic commands ###

View File

@@ -0,0 +1,24 @@
"""Add original_text column to recipes_ingredients
Revision ID: f1a2dbee5fe9
Revises: 263dd6707191
Create Date: 2022-03-27 19:30:28.545846
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "f1a2dbee5fe9"
down_revision = "263dd6707191"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("recipes_ingredients", sa.Column("original_text", sa.String(), nullable=True))
def downgrade():
op.drop_column("recipes_ingredients", "original_text")

View File

@@ -0,0 +1,57 @@
"""add tags to cookbooks
Revision ID: 59eb59135381
Revises: f1a2dbee5fe9
Create Date: 2022-03-31 19:19:55.428965
"""
import sqlalchemy as sa
import mealie.db.migration_types
from alembic import op
# revision identifiers, used by Alembic.
revision = "59eb59135381"
down_revision = "f1a2dbee5fe9"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"cookbooks_to_tags",
sa.Column("cookbook_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("tag_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["cookbook_id"],
["cookbooks.id"],
),
sa.ForeignKeyConstraint(
["tag_id"],
["tags.id"],
),
)
op.create_table(
"cookbooks_to_tools",
sa.Column("cookbook_id", mealie.db.migration_types.GUID(), nullable=True),
sa.Column("tool_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["cookbook_id"],
["cookbooks.id"],
),
sa.ForeignKeyConstraint(
["tool_id"],
["tools.id"],
),
)
op.add_column("cookbooks", sa.Column("public", sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("cookbooks", "public")
op.drop_table("cookbooks_to_tools")
op.drop_table("cookbooks_to_tags")
# ### end Alembic commands ###

View File

@@ -0,0 +1,45 @@
"""add require_all for cookbook filters
Revision ID: 09dfc897ad62
Revises: 59eb59135381
Create Date: 2022-04-03 10:48:51.379968
"""
import sqlalchemy as sa
import mealie.db.migration_types # noqa: F401
from alembic import op
# revision identifiers, used by Alembic.
revision = "09dfc897ad62"
down_revision = "59eb59135381"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("cookbooks", sa.Column("require_all_categories", sa.Boolean(), nullable=True))
op.add_column("cookbooks", sa.Column("require_all_tags", sa.Boolean(), nullable=True))
op.add_column("cookbooks", sa.Column("require_all_tools", sa.Boolean(), nullable=True))
# Set Defaults for Existing Cookbooks
op.execute(
"""
UPDATE cookbooks
SET require_all_categories = TRUE,
require_all_tags = TRUE,
require_all_tools = TRUE
"""
)
# ### end Alembic commands ###
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("cookbooks", "require_all_tools")
op.drop_column("cookbooks", "require_all_tags")
op.drop_column("cookbooks", "require_all_categories")
# ### end Alembic commands ###

View File

@@ -0,0 +1,30 @@
"""Add use_abbreviation column to ingredients
Revision ID: ab0bae02578f
Revises: 09dfc897ad62
Create Date: 2022-06-01 11:12:06.748383
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "ab0bae02578f"
down_revision = "09dfc897ad62"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("ingredient_units", sa.Column("use_abbreviation", sa.Boolean(), nullable=True))
op.execute("UPDATE ingredient_units SET use_abbreviation = FALSE WHERE use_abbreviation IS NULL")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("ingredient_units", "use_abbreviation")
# ### end Alembic commands ###

View File

@@ -0,0 +1,31 @@
"""add new webhook fields
Revision ID: f30cf048c228
Revises: ab0bae02578f
Create Date: 2022-06-15 21:05:34.851857
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "f30cf048c228"
down_revision = "ab0bae02578f"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("webhook_urls", sa.Column("webhook_type", sa.String(), nullable=True))
op.add_column("webhook_urls", sa.Column("scheduled_time", sa.Time(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("webhook_urls", "scheduled_time")
op.drop_column("webhook_urls", "webhook_type")
# ### end Alembic commands ###

64
cliff.toml Normal file
View File

@@ -0,0 +1,64 @@
# configuration file for git-cliff (0.1.0)
[changelog]
# changelog header
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://tera.netlify.app/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
# remove the leading and trailing whitespace from the template
trim = true
# changelog footer
footer = """
<!-- generated by git-cliff -->
"""
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = true
# regex for preprocessing the commit messages
commit_preprocessors = [
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/hay-kot/mealie/issues/${2}))"},
]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "Features"},
{ message = "^fix", group = "Bug Fixes"},
{ message = "^doc", group = "Documentation"},
{ message = "^perf", group = "Performance"},
{ message = "^refactor", group = "Refactor"},
{ message = "^style", group = "Styling"},
{ message = "^test", group = "Testing"},
{ message = "^chore\\(release\\): prepare for", skip = true},
{ message = "^chore", group = "Miscellaneous Tasks"},
{ body = ".*security", group = "Security"},
]
# filter out the commits that are not matched by commit parsers
filter_commits = false
# glob pattern for matching git tags
tag_pattern = "v[0-9]*"
# regex for skipping tags
skip_tags = "v0.1.0-beta.1"
# regex for ignoring tags
ignore_tags = ""
# sort the tags chronologically
date_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "oldest"

View File

@@ -1,6 +1,12 @@
preserve_hierarchy: false
files:
- source: /frontend/src/locales/messages/en-US.json
translation: /frontend/src/locales/messages/%locale%.json
- source: /frontend/src/locales/dateTimeFormats/en-US.json
translation: /frontend/src/locales/dateTimeFormats/%locale%.json
- source: /frontend/lang/messages/en-US.json
translation: /frontend/lang/messages/%locale%.json
- source: /mealie/lang/messages/en-US.json
translation: /mealie/lang/messages/%locale%.json
- source: /mealie/repos/seed/resources/foods/locales/en-US.json
translation: /mealie/repos/seed/resources/foods/locales/%locale%.json
- source: /mealie/repos/seed/resources/units/locales/en-US.json
translation: /mealie/repos/seed/resources/units/locales/%locale%.json
- source: /mealie/repos/seed/resources/labels/locales/en-US.json
translation: /mealie/repos/seed/resources/labels/locales/%locale%.json

View File

@@ -0,0 +1,100 @@
import re
from dataclasses import dataclass
from pathlib import Path
import black
import isort
from jinja2 import Template
def render_python_template(template_file: Path | str, dest: Path, data: dict) -> str:
"""Render and Format a Jinja2 Template for Python Code"""
if isinstance(template_file, Path):
tplt = Template(template_file.read_text())
else:
tplt = Template(template_file)
text = tplt.render(data=data)
text = black.format_str(text, mode=black.FileMode())
dest.write_text(text)
isort.file(dest)
@dataclass
class CodeSlicer:
start: int
end: int
indentation: str
text: list[str]
_next_line = None
def purge_lines(self) -> None:
start = self.start + 1
end = self.end
del self.text[start:end]
def push_line(self, string: str) -> None:
self._next_line = self._next_line or self.start + 1
print(self.indentation)
self.text.insert(self._next_line, self.indentation + string + "\n")
self._next_line += 1
def get_indentation_of_string(line: str, comment_char: str = "//") -> str:
return re.sub(rf"{comment_char}.*", "", line).removesuffix("\n")
def find_start_end(file_text: list[str], gen_id: str) -> tuple[int, int]:
start = None
end = None
indentation = None
for i, line in enumerate(file_text):
if "CODE_GEN_ID:" in line and gen_id in line:
start = i
indentation = get_indentation_of_string(line)
if f"END: {gen_id}" in line:
end = i
if start is None or end is None:
raise Exception("Could not find start and end of code generation block")
if start > end:
raise Exception(f"Start ({start=}) of code generation block is after end ({end=})")
return start, end, indentation
def inject_inline(file_path: Path, key: str, code: list[str]) -> None:
"""Injects a list of strings into the file where the key is found in the format defined
by the code-generation. Strings are properly indented and a '\n' is added to the end of
each string.
Start -> 'CODE_GEN_ID: <key>'
End -> 'END: <key>'
If no 'CODE_GEN_ID: <key>' is found, and exception is raised
Args:
file_path (Path): Write to file
key (str): CODE_GEN_ID: <key>
code (list[str]): List of strings to inject.
"""
with open(file_path, "r") as f:
file_text = f.readlines()
start, end, indentation = find_start_end(file_text, key)
slicer = CodeSlicer(start, end, indentation, file_text)
slicer.purge_lines()
for line in code:
slicer.push_line(line)
with open(file_path, "w") as file:
file.writelines(slicer.text)

View File

@@ -0,0 +1,131 @@
import json
import re
from pathlib import Path
from typing import Any
from _static import Directories
from fastapi import FastAPI
from humps import camelize
from slugify import slugify
def get_openapi_spec_by_ref(app, type_reference: str) -> dict:
if not type_reference:
return None
schemas = app["components"]["schemas"]
type_text = type_reference.split("/")[-1]
return schemas.get(type_text, type_reference)
def recursive_dict_search(data: dict[str, Any], key: str) -> Any:
"""
Walks a dictionary searching for a key and returns all the keys
matching the provided key"""
if key in data:
return data[key]
for _, v in data.items():
if isinstance(v, dict):
result = recursive_dict_search(v, key)
if result:
return result
return None
class APIFunction:
def __init__(self, app, route: str, verb: str, data: dict):
self.name_camel = camelize(data.get("summary"))
self.name_snake = slugify(data.get("summary"), separator="_")
self.http_verb = verb
self.path_vars = re.findall(r"\{(.*?)\}", route)
self.path_is_func = "{" in route
self.js_route = route.replace("{", "${")
self.py_route = route
self.body_schema = get_openapi_spec_by_ref(app, recursive_dict_search(data, "$ref"))
def path_args(self) -> str:
return ", ".join(x + ": string | number" for x in self.path_vars)
# body: Optional[list[str]] = []
# path_params: Optional[list[str]] = []
# query_params: Optional[list[str]] = []
# class APIModule(BaseModel):
# name: str
# functions: list[APIFunction]
class OpenAPIParser:
def __init__(self, app: FastAPI) -> None:
self.app = app
self.spec = app.openapi()
self.modules = {}
def dump(self, out_path: Path) -> Path:
"""Writes the Open API as JSON to a json file"""
OPEN_API_FILE = out_path or Directories.out_dir / "openapi.json"
with open(OPEN_API_FILE, "w") as f:
f.write(json.dumps(self.spec, indent=4))
def _group_by_module_tag(self):
"""
Itterates over all routes and groups them by module. Modules are determined
by the suffix text before : in the first tag for the router. These are used
to generate the typescript class interface for interacting with the API
"""
modules = {}
all_paths = self.spec["paths"]
for path, http_verbs in all_paths.items():
for _, value in http_verbs.items():
if "tags" in value:
tag: str = value["tags"][0]
if ":" in tag:
tag = tag.removeprefix('"').split(":")[0].replace(" ", "")
if modules.get(tag):
modules[tag][path] = http_verbs
else:
modules[tag] = {path: http_verbs}
return modules
def _get_openapi_spec(self, type_reference: str) -> dict:
schemas = self.app["components"]["schemas"]
type_text = type_reference.split("/")[-1]
return schemas.get(type_text, type_reference)
def _fill_schema_references(self, raw_modules: dict) -> dict:
for _, routes in raw_modules.items():
for _, verbs in routes.items():
for _, value in verbs.items():
if "requestBody" in value:
try:
schema_ref = value["requestBody"]["content"]["application/json"]["schema"]["$ref"]
schema = self._get_openapi_spec(schema_ref)
value["requestBody"]["content"]["application/json"]["schema"] = schema
except Exception:
continue
return raw_modules
def get_by_module(self) -> dict:
"""Returns paths where tags are split by : and left right is considered the module"""
raw_modules = self._group_by_module_tag()
modules = {}
for module_name, routes in raw_modules.items():
for route, verbs in routes.items():
for verb, value in verbs.items():
function = APIFunction(self.spec, route, verb, value)
if modules.get(module_name):
modules[module_name].append(function)
else:
modules[module_name] = [function]
return modules

View File

@@ -0,0 +1,86 @@
import re
from enum import Enum
from typing import Optional
from humps import camelize
from pydantic import BaseModel, Field
from slugify import slugify
class RouteObject:
def __init__(self, route_string) -> None:
self.prefix = "/" + route_string.split("/")[1]
self.route = "/" + route_string.split("/", 2)[2]
self.js_route = self.route.replace("{", "${")
self.parts = route_string.split("/")[1:]
self.var = re.findall(r"\{(.*?)\}", route_string)
self.is_function = "{" in self.route
self.router_slug = slugify("_".join(self.parts[1:]), separator="_")
self.router_camel = camelize(self.router_slug)
class RequestType(str, Enum):
get = "get"
put = "put"
post = "post"
patch = "patch"
delete = "delete"
class ParameterIn(str, Enum):
query = "query"
path = "path"
class RouterParameter(BaseModel):
required: bool = False
name: str
location: ParameterIn = Field(..., alias="in")
class RequestBody(BaseModel):
required: bool = False
class HTTPRequest(BaseModel):
request_type: RequestType
description: str = ""
summary: str
requestBody: Optional[RequestBody]
parameters: list[RouterParameter] = []
tags: list[str]
def list_as_js_object_string(self, parameters, braces=True):
if len(parameters) == 0:
return ""
if braces:
return "{" + ", ".join(parameters) + "}"
else:
return ", ".join(parameters)
def payload(self):
return "payload" if self.requestBody else ""
def function_args(self):
all_params = [p.name for p in self.parameters]
if self.requestBody:
all_params.append("payload")
return self.list_as_js_object_string(all_params)
def query_params(self):
params = [param.name for param in self.parameters if param.location == ParameterIn.query]
return self.list_as_js_object_string(params)
def path_params(self):
params = [param.name for param in self.parameters if param.location == ParameterIn.path]
return self.list_as_js_object_string(parameters=params, braces=False)
@property
def summary_camel(self):
return camelize(slugify(self.summary))
@property
def js_docs(self):
return self.description.replace("\n", " \n * ")

View File

@@ -0,0 +1,26 @@
from pathlib import Path
CWD = Path(__file__).parent
PROJECT_DIR = Path(__file__).parent.parent.parent
class Directories:
out_dir = CWD / "generated"
class CodeTemplates:
interface = CWD / "templates" / "interface.js"
pytest_routes = CWD / "templates" / "test_routes.py.j2"
class CodeDest:
interface = CWD / "generated" / "interface.js"
pytest_routes = CWD / "generated" / "test_routes.py"
use_locales = PROJECT_DIR / "frontend" / "composables" / "use-locales" / "available-locales.ts"
class CodeKeys:
"""Hard coded comment IDs that are used to generate code"""
nuxt_local_messages = "MESSAGE_LOCALES"
nuxt_local_dates = "DATE_LOCALES"

View File

@@ -0,0 +1,124 @@
from pathlib import Path
from jinja2 import Template
from pydantic2ts import generate_typescript_defs
from rich import print
# ============================================================
# Global Compoenents Generator
template = """// This Code is auto generated by gen_global_components.py
{% for name in global %} import {{ name }} from "@/components/global/{{ name }}.vue";
{% endfor %}
{% for name in layout %} import {{ name }} from "@/components/layout/{{ name }}.vue";
{% endfor %}
declare module "vue" {
export interface GlobalComponents {
// Global Components
{% for name in global %} {{ name }}: typeof {{ name }};
{% endfor %} // Layout Components
{% for name in layout %} {{ name }}: typeof {{ name }};
{% endfor %}
}
}
export {};
"""
CWD = Path(__file__).parent
PROJECT_DIR = Path(__file__).parent.parent.parent
def generate_global_components_types() -> None:
destination_file = PROJECT_DIR / "frontend" / "types" / "components.d.ts"
component_paths = {
"global": PROJECT_DIR / "frontend" / "components" / "global",
"layout": PROJECT_DIR / "frontend" / "components" / "Layout",
}
def render_template(template: str, data: dict) -> str | None:
tmpl = Template(template)
return tmpl.render(**data)
def build_data() -> dict:
data = {}
for name, path in component_paths.items():
components = [component.stem for component in path.glob("*.vue")]
data[name] = components
return data
def write_template(text: str) -> None:
destination_file.write_text(text)
text = render_template(template, build_data())
if text:
write_template(text)
# ============================================================
# Pydantic To Typescript Generator
def generate_typescript_types() -> None:
def path_to_module(path: Path):
str_path: str = str(path)
str_path = str_path.removeprefix(str(PROJECT_DIR))
str_path = str_path.removeprefix("/")
str_path = str_path.replace("/", ".")
return str_path
schema_path = PROJECT_DIR / "mealie" / "schema"
types_dir = PROJECT_DIR / "frontend" / "types" / "api-types"
ignore_dirs = ["__pycache__", "static", "_mealie"]
skipped_files: list[Path] = []
skipped_dirs: list[Path] = []
failed_modules: list[Path] = []
for module in schema_path.iterdir():
if module.is_dir() and module.stem in ignore_dirs:
skipped_dirs.append(module)
continue
if not module.is_dir() or not module.joinpath("__init__.py").is_file():
skipped_files.append(module)
continue
ts_out_name = module.name.replace("_", "-") + ".ts"
out_path = types_dir.joinpath(ts_out_name)
try:
path_as_module = path_to_module(module)
generate_typescript_defs(path_as_module, str(out_path), exclude=("MealieModel")) # type: ignore
except Exception as e:
failed_modules.append(module)
print("\nModule Errors:", module, "-----------------") # noqa
print(e) # noqa
print("Finished Module Errors:", module, "-----------------\n") # noqa
print("\n📁 Skipped Directories:") # noqa
for skipped_dir in skipped_dirs:
print(" 📁", skipped_dir.name) # noqa
print("📄 Skipped Files:") # noqa
for f in skipped_files:
print(" 📄", f.name) # noqa
print("❌ Failed Modules:") # noqa
for f in failed_modules:
print("", f.name) # noqa
if __name__ == "__main__":
print("\n-- Starting Global Components Generator --") # noqa
generate_global_components_types()
print("\n-- Starting Pydantic To Typescript Generator --") # noqa
generate_typescript_types()

View File

@@ -0,0 +1,144 @@
import pathlib
import _static
import dotenv
import requests
from jinja2 import Template
from requests import Response
from rich import print
from mealie.schema._mealie import MealieModel
BASE = pathlib.Path(__file__).parent.parent.parent
API_KEY = dotenv.get_key(BASE / ".env", "CROWDIN_API_KEY")
NAMES = {
"en-US": "American English",
"en-GB": "British English",
"af-ZA": "Afrikaans (Afrikaans)",
"ar-SA": "العربية (Arabic)",
"ca-ES": "Català (Catalan)",
"cs-CZ": "Čeština (Czech)",
"da-DK": "Dansk (Danish)",
"de-DE": "Deutsch (German)",
"el-GR": "Ελληνικά (Greek)",
"es-ES": "Español (Spanish)",
"fi-FI": "Suomi (Finnish)",
"fr-FR": "Français (French)",
"he-IL": "עברית (Hebrew)",
"hu-HU": "Magyar (Hungarian)",
"it-IT": "Italiano (Italian)",
"ja-JP": "日本語 (Japanese)",
"ko-KR": "한국어 (Korean)",
"no-NO": "Norsk (Norwegian)",
"nl-NL": "Nederlands (Dutch)",
"pl-PL": "Polski (Polish)",
"pt-BR": "Português do Brasil (Brazilian Portuguese)",
"pt-PT": "Português (Portugese)",
"ro-RO": "Română (Romanian)",
"ru-RU": "Pусский (Russian)",
"sr-SP": "српски (Serbian)",
"sv-SE": "Svenska (Swedish)",
"tr-TR": "Türkçe (Turkish)",
"uk-UA": "Українська (Ukrainian)",
"vi-VN": "Tiếng Việt (Vietnamese)",
"zh-CN": "简体中文 (Chinese simplified)",
"zh-TW": "繁體中文 (Chinese traditional)",
}
LOCALE_TEMPLATE = """// This Code is auto generated by gen_global_components.py
export const LOCALES = [{% for locale in locales %}
{
name: "{{ locale.name }}",
value: "{{ locale.locale }}",
progress: {{ locale.progress }},
},{% endfor %}
]
"""
class TargetLanguage(MealieModel):
id: str
name: str
locale: str
threeLettersCode: str
twoLettersCode: str
progress: float = 0.0
class CrowdinApi:
project_name = "Mealie"
project_id = "451976"
api_key = API_KEY
def __init__(self, api_key: str):
api_key = api_key
@property
def headers(self) -> dict:
return {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
}
def get_projects(self) -> Response:
return requests.get("https://api.crowdin.com/api/v2/projects", headers=self.headers)
def get_project(self) -> Response:
return requests.get(f"https://api.crowdin.com/api/v2/projects/{self.project_id}", headers=self.headers)
def get_languages(self) -> list[TargetLanguage]:
response = self.get_project()
tls = response.json()["data"]["targetLanguages"]
models = [TargetLanguage(**t) for t in tls]
models.insert(
0,
TargetLanguage(
id="en-US", name="English", locale="en-US", threeLettersCode="en", twoLettersCode="en", progress=100
),
)
progress: list[dict] = self.get_progress()["data"]
for model in models:
if model.locale in NAMES:
model.name = NAMES[model.locale]
for p in progress:
if p["data"]["languageId"] == model.id:
model.progress = p["data"]["translationProgress"]
models.sort(key=lambda x: x.locale, reverse=True)
return models
def get_progress(self) -> dict:
response = requests.get(
f"https://api.crowdin.com/api/v2/projects/{self.project_id}/languages/progress?limit=500",
headers=self.headers,
)
return response.json()
def main():
print("Starting...") # noqa
if API_KEY is None:
print("CROWDIN_API_KEY is not set") # noqa
return
api = CrowdinApi("")
models = api.get_languages()
tmpl = Template(LOCALE_TEMPLATE)
rendered = tmpl.render(locales=models)
with open(_static.CodeDest.use_locales, "w") as f:
f.write(rendered) # type:ignore
print("Finished...") # noqa
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,39 @@
from pathlib import Path
from _gen_utils import inject_inline
from _static import CodeKeys
PROJECT_DIR = Path(__file__).parent.parent.parent
datetime_dir = PROJECT_DIR / "frontend" / "lang" / "dateTimeFormats"
locales_dir = PROJECT_DIR / "frontend" / "lang" / "messages"
nuxt_config = PROJECT_DIR / "frontend" / "nuxt.config.js"
"""
This snippet walks the message and dat locales directories and generates the import information
for the nuxt.config.js file and automatically injects it into the nuxt.config.js file. Note that
the code generation ID is hardcoded into the script and required in the nuxt config.
"""
def main(): # sourcery skip: list-comprehension
print("Starting...")
all_date_locales = []
for match in datetime_dir.glob("*.json"):
all_date_locales.append(f'"{match.stem}": require("./lang/dateTimeFormats/{match.name}"),')
all_langs = []
for match in locales_dir.glob("*.json"):
lang_string = f'{{ code: "{match.stem}", file: "{match.name}" }},'
all_langs.append(lang_string)
inject_inline(nuxt_config, CodeKeys.nuxt_local_messages, all_langs)
inject_inline(nuxt_config, CodeKeys.nuxt_local_dates, all_date_locales)
print("Finished...")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,53 @@
import json
from typing import Any
from _gen_utils import render_python_template
from _open_api_parser import OpenAPIParser
from _static import CodeDest, CodeTemplates
from rich.console import Console
from mealie.app import app
"""
This code is used for generating route objects for each route in the OpenAPI Specification.
Currently, they are NOT automatically injected into the test suite. As such, you'll need to copy
the relavent contents of the generated file into the test suite where applicable. I am slowly
migrating the test suite to use this new generated file and this process will be "automated" in the
future.
"""
console = Console()
def write_dict_to_file(file_name: str, data: dict[str, Any]):
with open(file_name, "w") as f:
f.write(json.dumps(data, indent=4))
def main():
print("Starting...")
open_api = OpenAPIParser(app)
modules = open_api.get_by_module()
mods = []
for mod, value in modules.items():
routes = []
existings = set()
# Reduce routes by unique py_route attribute
for route in value:
if route.py_route not in existings:
existings.add(route.py_route)
routes.append(route)
module = {"name": mod, "routes": routes}
mods.append(module)
render_python_template(CodeTemplates.pytest_routes, CodeDest.pytest_routes, {"mods": mods})
print("Finished...")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,35 @@
from _gen_utils import render_python_template
from _static import PROJECT_DIR
template = """# GENERATED CODE - DO NOT MODIFY BY HAND
{% for file in data.files %}from .{{ file }} import *
{% endfor %}
"""
SCHEMA_PATH = PROJECT_DIR / "mealie" / "schema"
def generate_init_files() -> None:
for schema in SCHEMA_PATH.iterdir():
if not schema.is_dir():
print(f"Skipping {schema}")
continue
print(f"Generating {schema}")
init_file = schema.joinpath("__init__.py")
module_files = [
f.stem for f in schema.iterdir() if f.is_file() and f.suffix == ".py" and not f.stem.startswith("_")
]
render_python_template(template, init_file, {"files": module_files})
def main():
print("Starting...")
generate_init_files()
print("Finished...")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,124 @@
from dataclasses import dataclass
from pathlib import Path
from _gen_utils import render_python_template
from slugify import slugify
CWD = Path(__file__).parent
TEMPLATE = CWD / "templates" / "test_data.py.j2"
TEST_DATA = CWD.parent.parent / "tests" / "data"
GENERATED = CWD / "generated"
@dataclass
class TestDataPath:
var: str
path: str
@classmethod
def from_path(cls, path: Path):
var = str(path)
var = var.replace(str(TEST_DATA), "")
rel_path = var.removeprefix("/")
# Remove any file extension
var = var.split(".")[0]
var = var.replace("'", "")
var = slugify(var, separator="_")
return cls(var, rel_path)
@dataclass
class DataDir:
name: str
path: Path
children: list[TestDataPath]
def get_data_paths(path: Path) -> list[DataDir]:
"""
Recursively walks the given path and returns a list of TestDataPaths
"""
def recursive_test_paths(p: Path) -> list[TestDataPath]:
test_paths = []
for child in p.iterdir():
if child.is_dir():
test_paths += recursive_test_paths(child)
else:
test_paths.append(TestDataPath.from_path(child))
return [x for x in test_paths if not None]
data_paths = []
for p in path.iterdir():
if p.is_dir():
data_paths.append(DataDir(p.name, p, recursive_test_paths(p)))
return data_paths
def rename_non_compliant_paths():
"""
Recursively itterates through a directory and renames all files/folders to be
kabab case.
"""
ignore_files = ["DS_Store", ".gitkeep"]
ignore_extensions = [".pyc", ".pyo", ".py"]
def recursive_rename(p: Path):
for child in p.iterdir():
if str(child).startswith("."):
continue
if child.suffix in ignore_extensions:
continue
if child.name in ignore_files:
continue
if child.is_dir():
recursive_rename(child)
else:
new_name = slugify(child.stem)
extension = child.suffix
if new_name != child.name:
child.rename(child.parent / f"{new_name}{extension}")
recursive_rename(TEST_DATA)
def main():
print("Starting Template Generation")
rename_non_compliant_paths()
GENERATED.mkdir(exist_ok=True)
data_dirs = get_data_paths(TEST_DATA)
all_children = [x.children for x in data_dirs]
# Flatten list of lists
all_children = [item for sublist in all_children for item in sublist]
render_python_template(
TEMPLATE,
GENERATED / "__init__.py",
{"children": all_children},
)
print("Finished Template Generation")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,7 @@
from pathlib import Path
CWD = Path(__file__).parent
{% for f in data.children %}
{{ f.var }} = CWD / "{{ f.path }}"
{% endfor %}

View File

@@ -0,0 +1,9 @@
{% for mod in mods %}
class {{mod.name}}Routes:{% for route in mod.routes %}{% if not route.path_is_func %}
{{route.name_snake}} = "{{ route.py_route }}"{% endif %}{% endfor %}{% for route in mod.routes %}
{% if route.path_is_func %}
@staticmethod
def {{route.name_snake}}({{ route.path_vars|join(", ") }}):
return f"{{route.py_route}}"
{% endif %}{% endfor %}
{% endfor %}

View File

@@ -0,0 +1,62 @@
import json
import random
import string
import time
import requests
def random_string(length: int) -> str:
return "".join(random.choice(string.ascii_lowercase) for _ in range(length))
def payload_factory() -> dict:
return {"name": random_string(15)}
def login(username="changeme@email.com", password="MyPassword"):
payload = {"username": username, "password": password}
r = requests.post("http://localhost:9000/api/auth/token", payload)
# Bearer
token = json.loads(r.text).get("access_token")
return {"Authorization": f"Bearer {token}"}
def populate_data(token):
for _ in range(300):
payload = payload_factory()
r = requests.post("http://localhost:9000/api/recipes", json=payload, headers=token)
if r.status_code != 201:
print(f"Error: {r.status_code}")
print(r.text)
exit()
else:
print(f"Created recipe: {payload}")
def time_request(url, headers):
start = time.time()
r = requests.get(url, headers=headers)
print(f"Total Recipes {len(r.json())}")
end = time.time()
print(end - start)
def main():
print("Starting...")
token = login()
# populate_data(token)
for _ in range(10):
time_request("http://localhost:9000/api/recipes", token)
print("Finished...")
if __name__ == "__main__":
main()

View File

@@ -1,7 +1,9 @@
import json
from mealie.app import app
from mealie.core.config import DATA_DIR
from mealie.core.config import determine_data_dir
DATA_DIR = determine_data_dir()
"""Script to export the ReDoc documentation page into a standalone HTML file."""

View File

@@ -1,28 +1,23 @@
import json
import re
from enum import Enum
from itertools import groupby
from pathlib import Path
from typing import Optional
import slugify
from fastapi import FastAPI
from humps import camelize
from jinja2 import Template
from pydantic import BaseModel, Field
from slugify import slugify
from mealie.app import app
from pydantic import BaseModel
CWD = Path(__file__).parent
OUT_DIR = CWD / "output"
OUT_FILE = OUT_DIR / "app_routes.py"
JS_DIR = OUT_DIR / "javascriptAPI"
JS_OUT_FILE = JS_DIR / "apiRoutes.js"
TEMPLATES_DIR = CWD / "templates"
PYTEST_TEMPLATE = TEMPLATES_DIR / "pytest_routes.j2"
JS_REQUESTS = TEMPLATES_DIR / "js_requests.j2"
JS_ROUTES = TEMPLATES_DIR / "js_routes.j2"
JS_INDEX = TEMPLATES_DIR / "js_index.j2"
JS_DIR = OUT_DIR / "javascriptAPI"
JS_DIR.mkdir(exist_ok=True, parents=True)
@@ -34,17 +29,9 @@ class RouteObject:
self.parts = route_string.split("/")[1:]
self.var = re.findall(r"\{(.*?)\}", route_string)
self.is_function = "{" in self.route
self.router_slug = slugify.slugify("_".join(self.parts[1:]), separator="_")
self.router_slug = slugify("_".join(self.parts[1:]), separator="_")
self.router_camel = camelize(self.router_slug)
def __repr__(self) -> str:
return f"""Route: {self.route}
Parts: {self.parts}
Function: {self.is_function}
Var: {self.var}
Slug: {self.router_slug}
"""
class RequestType(str, Enum):
get = "get"
@@ -54,15 +41,59 @@ class RequestType(str, Enum):
delete = "delete"
class ParameterIn(str, Enum):
query = "query"
path = "path"
class RouterParameter(BaseModel):
required: bool = False
name: str
location: ParameterIn = Field(..., alias="in")
class RequestBody(BaseModel):
required: bool = False
class HTTPRequest(BaseModel):
request_type: RequestType
description: str = ""
summary: str
requestBody: Optional[RequestBody]
parameters: list[RouterParameter] = []
tags: list[str]
def list_as_js_object_string(self, parameters, braces=True):
if len(parameters) == 0:
return ""
if braces:
return "{" + ", ".join(parameters) + "}"
else:
return ", ".join(parameters)
def payload(self):
return "payload" if self.requestBody else ""
def function_args(self):
all_params = [p.name for p in self.parameters]
if self.requestBody:
all_params.append("payload")
return self.list_as_js_object_string(all_params)
def query_params(self):
params = [param.name for param in self.parameters if param.location == ParameterIn.query]
return self.list_as_js_object_string(params)
def path_params(self):
params = [param.name for param in self.parameters if param.location == ParameterIn.path]
return self.list_as_js_object_string(parameters=params, braces=False)
@property
def summary_camel(self):
return camelize(self.summary)
return camelize(slugify(self.summary))
@property
def js_docs(self):
@@ -94,40 +125,68 @@ def get_path_objects(app: FastAPI):
return paths
def dump_open_api(app: FastAPI):
"""Writes the Open API as JSON to a json file"""
OPEN_API_FILE = CWD / "openapi.json"
with open(OPEN_API_FILE, "w") as f:
f.write(json.dumps(app.openapi()))
def read_template(file: Path):
with open(file, "r") as f:
return f.read()
def generate_template(app):
paths = get_path_objects(app)
static_paths = [x.route_object for x in paths if not x.route_object.is_function]
function_paths = [x.route_object for x in paths if x.route_object.is_function]
static_paths.sort(key=lambda x: x.router_slug)
function_paths.sort(key=lambda x: x.router_slug)
def generate_python_templates(static_paths: list[PathObject], function_paths: list[PathObject]):
PYTEST_TEMPLATE = TEMPLATES_DIR / "pytest_routes.j2"
PYTHON_OUT_FILE = OUT_DIR / "app_routes.py"
template = Template(read_template(PYTEST_TEMPLATE))
content = template.render(paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths})
with open(OUT_FILE, "w") as f:
content = template.render(
paths={
"prefix": "/api",
"static_paths": static_paths,
"function_paths": function_paths,
}
)
with open(PYTHON_OUT_FILE, "w") as f:
f.write(content)
template = Template(read_template(JS_ROUTES))
content = template.render(
paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths, "all_paths": paths}
)
with open(JS_OUT_FILE, "w") as f:
f.write(content)
return
def generate_js_templates(paths: list[PathObject]):
# Template Path
JS_API_INTERFACE = TEMPLATES_DIR / "js_api_interface.j2"
JS_INDEX = TEMPLATES_DIR / "js_index.j2"
INTERFACES_DIR = JS_DIR / "interfaces"
INTERFACES_DIR.mkdir(exist_ok=True, parents=True)
all_tags = []
for k, g in groupby(paths, lambda x: x.http_verbs[0].tags[0]):
template = Template(read_template(JS_REQUESTS))
content = template.render(paths={"all_paths": list(g), "export_name": camelize(k)})
for tag, tag_paths in groupby(paths, lambda x: x.http_verbs[0].tags[0]):
file_name = slugify(tag, separator="-")
all_tags.append(camelize(k))
tag = camelize(tag)
with open(JS_DIR.joinpath(camelize(k) + ".js"), "w") as f:
tag_paths: list[PathObject] = list(tag_paths)
template = Template(read_template(JS_API_INTERFACE))
content = template.render(
paths={
"prefix": "/api",
"static_paths": [x.route_object for x in tag_paths if not x.route_object.is_function],
"function_paths": [x.route_object for x in tag_paths if x.route_object.is_function],
"all_paths": tag_paths,
"export_name": tag,
}
)
tag: dict = {"camel": camelize(tag), "slug": file_name}
all_tags.append(tag)
with open(INTERFACES_DIR.joinpath(file_name + ".ts"), "w") as f:
f.write(content)
template = Template(read_template(JS_INDEX))
@@ -137,5 +196,19 @@ def generate_template(app):
f.write(content)
def generate_template(app):
dump_open_api(app)
paths = get_path_objects(app)
static_paths = [x.route_object for x in paths if not x.route_object.is_function]
function_paths = [x.route_object for x in paths if x.route_object.is_function]
static_paths.sort(key=lambda x: x.router_slug)
function_paths.sort(key=lambda x: x.router_slug)
generate_python_templates(static_paths, function_paths)
generate_js_templates(paths)
if __name__ == "__main__":
generate_template(app)

View File

@@ -0,0 +1,26 @@
import json
from pathlib import Path
import requests
CWD = Path(__file__).parent
def login(username="changeme@email.com", password="MyPassword"):
payload = {"username": username, "password": password}
r = requests.post("http://localhost:9000/api/auth/token", payload)
# Bearer
token = json.loads(r.text).get("access_token")
return {"Authorization": f"Bearer {token}"}
def main():
print("Starting...")
print("Finished...")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,159 @@
import json
import re
from dataclasses import dataclass
from pathlib import Path
from slugify import slugify
CWD = Path(__file__).parent
PROJECT_BASE = CWD.parent.parent
server_side_msgs = PROJECT_BASE / "mealie" / "utils" / "error_messages.py"
en_us_msgs = PROJECT_BASE / "frontend" / "lang" / "errors" / "en-US.json"
client_side_msgs = PROJECT_BASE / "frontend" / "utils" / "error-messages.ts"
GENERATE_MESSAGES = [
# User Related
"user",
"webhook",
"token",
# Group Related
"group",
"cookbook",
"mealplan",
# Recipe Related
"scraper",
"recipe",
"ingredient",
"food",
"unit",
# Admin Related
"backup",
"migration",
"event",
]
class ErrorMessage:
def __init__(self, prefix, verb) -> None:
self.message = f"{prefix.title()} {verb.title()} Failed"
self.snake = slugify(self.message, separator="_")
self.kabab = slugify(self.message, separator="-")
def factory(prefix) -> list["ErrorMessage"]:
verbs = ["Create", "Update", "Delete"]
return [ErrorMessage(prefix, verb) for verb in verbs]
@dataclass
class CodeGenLines:
start: int
end: int
indentation: str
text: list[str]
_next_line = None
def purge_lines(self) -> None:
start = self.start + 1
end = self.end
del self.text[start:end]
def push_line(self, string: str) -> None:
self._next_line = self._next_line or self.start + 1
self.text.insert(self._next_line, self.indentation + string)
self._next_line += 1
def find_start(file_text: list[str], gen_id: str):
for x, line in enumerate(file_text):
if "CODE_GEN_ID:" in line and gen_id in line:
return x, line
return None
def find_end(file_text: list[str], gen_id: str):
for x, line in enumerate(file_text):
if f"END {gen_id}" in line:
return x, line
return None
def get_indentation_of_string(line: str):
return re.sub(r"#.*", "", line).removesuffix("\n")
def get_messages(message_prefix: str) -> str:
prefix = message_prefix.lower()
return [
f'{prefix}_create_failure = "{prefix}-create-failure"\n',
f'{prefix}_update_failure = "{prefix}-update-failure"\n',
f'{prefix}_delete_failure = "{prefix}-delete-failure"\n',
]
def code_gen_factory(file_path: Path) -> CodeGenLines:
with open(file_path, "r") as file:
text = file.readlines()
start_num, line = find_start(text, "ERROR_MESSAGE_ENUMS")
indentation = get_indentation_of_string(line)
end_num, line = find_end(text, "ERROR_MESSAGE_ENUMS")
return CodeGenLines(
start=start_num,
end=end_num,
indentation=indentation,
text=text,
)
def write_to_locals(messages: list[ErrorMessage]) -> None:
with open(en_us_msgs, "r") as f:
existing_msg = json.loads(f.read())
for msg in messages:
if msg.kabab in existing_msg:
continue
existing_msg[msg.kabab] = msg.message
print(f"Added Key {msg.kabab} to 'en-US.json'")
with open(en_us_msgs, "w") as f:
f.write(json.dumps(existing_msg, indent=4))
def main():
print("Starting...")
GENERATE_MESSAGES.sort()
code_gen = code_gen_factory(server_side_msgs)
code_gen.purge_lines()
messages = []
for msg_type in GENERATE_MESSAGES:
messages += get_messages(msg_type)
messages.append("\n")
for msg in messages:
code_gen.push_line(msg)
with open(server_side_msgs, "w") as file:
file.writelines(code_gen.text)
# Locals
local_msgs = []
for msg_type in GENERATE_MESSAGES:
local_msgs += ErrorMessage.factory(msg_type)
write_to_locals(local_msgs)
print("Done!")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,32 @@
import json
import requests
from pydantic import BaseModel
class GithubIssue(BaseModel):
url: str
number: int
title: str
def get_issues_by_label(label="fixed-pending-release") -> list[GithubIssue]:
issues_url = f"https://api.github.com/repos/hay-kot/mealie/issues?labels={label}"
response = requests.get(issues_url)
issues = json.loads(response.text)
return [GithubIssue(**issue) for issue in issues]
def format_markdown_list(issues: list[GithubIssue]) -> str:
return "\n".join(f"- [{issue.number}]({issue.url}) - {issue.title}" for issue in issues)
def main() -> None:
issues = get_issues_by_label()
print(format_markdown_list(issues))
if __name__ == "__main__":
main()

View File

@@ -30,11 +30,11 @@ class AppRoutes:
self.meal_plans_today = "/api/meal-plans/today"
self.meal_plans_today_image = "/api/meal-plans/today/image"
self.migrations = "/api/migrations"
self.recipes = "/api/recipes"
self.recipes_category = "/api/recipes/category"
self.recipes_create = "/api/recipes/create"
self.recipes_create_from_zip = "/api/recipes/create-from-zip"
self.recipes_create_url = "/api/recipes/create-url"
self.recipes_summary = "/api/recipes/summary"
self.recipes_summary_uncategorized = "/api/recipes/summary/uncategorized"
self.recipes_summary_untagged = "/api/recipes/summary/untagged"
self.recipes_tag = "/api/recipes/tag"

View File

@@ -0,0 +1,17 @@
import { requests } from "../requests";
const prefix = '{{paths.prefix}}'
const routes = { {% for path in paths.static_paths %}
{{ path.router_camel }}: `${prefix}{{ path.route }}`,{% endfor %}
{% for path in paths.function_paths %}
{{path.router_camel}}: ({{path.var|join(", ")}}) => `${prefix}{{ path.js_route }}`,{% endfor %}
}
export const {{paths.export_name}}API = { {% for path in paths.all_paths %} {% for verb in path.http_verbs %}
{% if verb.js_docs %}/** {{ verb.js_docs }}
*/ {% endif %}
async {{ verb.summary_camel }}({{ verb.function_args() }}) {
return await requests.{{ verb.request_type.value }}(routes.{{ path.route_object.router_camel }}{% if path.route_object.is_function %}({{verb.path_params()}}){% endif %}, {{ verb.query_params() }} {{ verb.payload() }})
}, {% endfor %} {% endfor %}
}

View File

@@ -1,7 +1,7 @@
{% for api in files.files %}
import { {{ api }}API } from "./{{api}}.js" {% endfor %}
import { {{ api.camel }}API } from "./interfaces/{{ api.slug }}" {% endfor %}
export const api = {
{% for api in files.files %}
{{api}}: {{api}}API, {% endfor %}
{{api.camel}}: {{api.camel}}API, {% endfor %}
}

View File

@@ -1,19 +0,0 @@
// This Content is Auto Generated
import { API_ROUTES } from "./apiRoutes"
export const {{paths.export_name}}API = { {% for path in paths.all_paths %} {% for verb in path.http_verbs %} {% if path.route_object.is_function %}
/** {{ verb.js_docs }} {% for v in path.route_object.var %}
* @param {{ v }} {% endfor %}
*/
{{ verb.summary_camel }}({{path.route_object.var|join(", ")}}) {
const response = await apiReq.{{ verb.request_type.value }}(API_ROUTES.{{ path.route_object.router_camel }}({{path.route_object.var|join(", ")}}))
return response.data
}, {% else %}
/** {{ verb.js_docs }} {% for v in path.route_object.var %}
* @param {{ v }} {% endfor %}
*/
{{ verb.summary_camel }}() {
const response = await apiReq.{{ verb.request_type.value }}(API_ROUTES.{{ path.route_object.router_camel }})
return response.data
},{% endif %} {% endfor %} {% endfor %}
}

View File

@@ -1,7 +0,0 @@
// This Content is Auto Generated
const prefix = '{{paths.prefix}}'
export const API_ROUTES = { {% for path in paths.static_paths %}
{{ path.router_camel }}: `${prefix}{{ path.route }}`,{% endfor %}
{% for path in paths.function_paths %}
{{path.router_camel}}: ({{path.var|join(", ")}}) => `${prefix}{{ path.js_route }}`,{% endfor %}
}

View File

@@ -1,5 +1,6 @@
# WARNING: currently not functional, see #756, #1072
# Use root/example as user/password credentials
version: "3.1"
version: "3.4"
services:
# Vue Frontend
mealie-frontend:
@@ -7,12 +8,14 @@ services:
image: mealie-frontend:dev
build:
context: ./frontend
dockerfile: frontend.Dockerfile
dockerfile: Dockerfile.frontend
restart: always
ports:
- 9920:8080
environment:
VUE_APP_API_BASE_URL: "http://mealie-api:9000"
- GLOBAL_MIDDLEWARE=null
- BASE_URL=""
- ALLOW_SIGNUP=true
volumes:
- ./frontend/:/app
- /app/node_modules

View File

@@ -1,30 +1,84 @@
version: "3.1"
version: "3.4"
services:
mealie-frontend:
container_name: mealie-frontend
image: mealie-frontend:dev
build:
context: ./frontend
dockerfile: Dockerfile
restart: always
volumes:
- mealie-data:/app/data/
ports:
- 9091:3000
environment:
- API_URL=http://mealie-api:9000
# =====================================
# Light Mode Config
- THEME_LIGHT_PRIMARY=#E58325
- THEME_LIGHT_ACCENT=#007A99
- THEME_LIGHT_SECONDARY=#973542
- THEME_LIGHT_SUCCESS=#43A047
- THEME_LIGHT_INFO=#1976D2
- THEME_LIGHT_WARNING=#FF6D00
- THEME_LIGHT_ERROR=#EF5350
# =====================================
# Dark Mode Config
- THEME_DARK_PRIMARY=#E58325
- THEME_DARK_ACCENT=#007A99
- THEME_DARK_SECONDARY=#973542
- THEME_DARK_SUCCESS=#43A047
- THEME_DARK_INFO=#1976D2
- THEME_DARK_WARNING=#FF6D00
- THEME_DARK_ERROR=#EF5350
mealie:
container_name: mealie-api
build:
context: ./
target: production
dockerfile: Dockerfile
container_name: mealie
restart: always
depends_on:
- "postgres"
volumes:
- mealie-data:/app/data/
ports:
- 9090:80
- 9092:9000
environment:
DB_ENGINE: postgres # Optional: 'sqlite', 'postgres'
ALLOW_SIGNUP: "false"
DB_ENGINE: sqlite # Optional: 'sqlite', 'postgres'
# =====================================
# Postgres Config
POSTGRES_USER: mealie
POSTGRES_PASSWORD: mealie
POSTGRES_SERVER: postgres
POSTGRES_PORT: 5432
POSTGRES_DB: mealie
# WORKERS_PER_CORE: 0.5
# MAX_WORKERS: 8
WEB_CONCURRENCY: 2
postgres:
container_name: postgres
image: postgres
restart: always
environment:
POSTGRES_PASSWORD: mealie
POSTGRES_USER: mealie
# =====================================
# Web Concurrency
WORKERS_PER_CORE: 0.5
MAX_WORKERS: 1
WEB_CONCURRENCY: 1
# =====================================
# Email Configuration
# SMTP_HOST=
# SMTP_PORT=587
# SMTP_FROM_NAME=Mealie
# SMTP_AUTH_STRATEGY=TLS # Options: 'TLS', 'SSL', 'NONE'
# SMTP_FROM_EMAIL=
# SMTP_USER=
# SMTP_PASSWORD=
# postgres:
# container_name: postgres
# image: postgres
# restart: always
# environment:
# POSTGRES_PASSWORD: mealie
# POSTGRES_USER: mealie
volumes:
mealie-data:
driver: local

View File

@@ -1,15 +0,0 @@
{
auto_https off
}
:80 {
root * /srv
encode gzip
uri strip_suffix /
handle {
try_files {path} {path}/ /index.html
file_server
}
}

View File

@@ -1,10 +0,0 @@
FROM python:3.8-slim as build-stage
WORKDIR /app
RUN pip install --no-cache-dir mkdocs mkdocs-material
COPY . .
RUN mkdocs build
FROM caddy:alpine
WORKDIR /app
COPY ./Caddyfile /etc/caddy/Caddyfile
COPY --from=build-stage /app/site /srv

View File

@@ -1,11 +0,0 @@
version: "3"
services:
wiki:
container_name: mealie-docs
image: mealie-docs
ports:
- 8888:80
build:
context: .
dockerfile: Dockerfile
restart: always

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,294 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800px" height="377px" viewBox="-0.5 -0.5 800 377" content="&lt;mxfile&gt;&lt;diagram id=&quot;3dpkjlI-_KjnuoPEQ-Vt&quot; name=&quot;Page-1&quot;&gt;5VlNU9swEP01maGHeiwp/jpCAuXQTpmhM22PwhaJB8fyKApJ+uu7jqVYihxIMRAoPiTW6sPye29XK3lARrPVF0Gr6TeesWKA/Ww1IOMBxsiPMPzVlrWywNVYJiLPlK01XOd/mO6qrIs8Y3OroeS8kHllG1NeliyVlo0KwZd2s1te2E+t6IQ5huuUFq71Z57JaWONA7+1X7J8MtVPRr6qmVHdWBnmU5rxpWEi5wMyEpzL5m62GrGiRk/j0vS72FO7nZhgpTykAw6aHve0WKiXUxOTa/22gi/KjNUd/AE5W05zya4rmta1SyAYbFM5K6CE4FYNx4Rkq71zQts3BY0wPmNSrKGJ6oDDpodWx7ApLlukoyRpbFMDZRKpflSxO9mO3AIANwqDbjzIi8DhvHsHQvvhiCMvDiI0xM1vYqEzjD2jLg5iB6wAR57RQDufBR1GntECDYf9kRw6SI55eseEgyfAIG3Q5lLwOzbiBRdgKXkJLc9u86LYMdEin5RQTAFcGJic1aDm4KanqmKWZ1n9mE6WbB5veSmv1aT2KfafWbOFrH3eFLLv4Q4p45rU/hS4vj1jgBn7TKv8AVmjV5F1GGKvQ7gKq9i3KrVoDegQgGS6hQbbxDEcerjDdfpgilxQL3/8uHLgZBmsGKrIihu+PG8NByE55wuRqsGUFiQVEyYtbuvHPIi2YAWV+b29cPUBINonqlsBLsTK7OjKGiL/gYD5ZpUV7wM2o5I6oELiUNW36brIAV1BHof2puHh683WQNO7yYad7wsJwzAdf5uEC6T+PJ4eh10LkI6K+NEVLO5YshAKOnns5dsuBdCFVhX8dpLwrE4euE4eH8vJkevlO0hcPBkMwECsf6lld1P4XRe8QBfHK7NyvDZLV0zk8Db1ar8xPjV8Pjuym66nQtC10aDieSnnxshXtcGIVFFghRFCdhIG3BWIWg6b57WMbid+GMm+Q/II4k09wSOnvEFg4ZCEXhQnxuVEiMTfJkx2XuuhZ0hlEXl1sR+ka71rNoUdHS1kaJZbNSUJBDVySny9/T1iUgCzsV3NUth2g26mAcjeN+nMz0rUdxYh3aYXkO7OqVeG+bIS9F0JaiUcQYOJA90J+uQg93/uOAm2NR11p6g7u8+OFlYE7U7O+pCE3WXnBH8UkoIE2yct74MyN7afkA9DGSYeMYO8naMFxPPNi7wVzvAH5iwivheaiNqrfRA+tuU8Emfu8fMIkKGwNRfvh7leh/+bwxv73HQPPS93btpxdv3RaGhOZ1+VBii2372a7Wz7+ZCc/wU=&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="0" y="0" width="799" height="376" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<rect x="261.86" y="34.57" width="527.14" height="321.43" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<rect x="261.86" y="6" width="70.29" height="28.57" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 20px; margin-left: 263px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
Docker
</div>
</div>
</div>
</foreignObject>
<text x="297" y="24" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold">
Docker
</text>
</switch>
</g>
<rect x="636.71" y="66.71" width="128.86" height="64.29" rx="9.64" ry="9.64" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 127px; height: 1px; padding-top: 99px; margin-left: 638px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
mealie-api
</div>
</div>
</div>
</foreignObject>
<text x="701" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
mealie-api
</text>
</switch>
</g>
<path d="M 513.71 98.86 L 565.33 98.86 Q 575.33 98.86 585.33 98.86 L 630.35 98.86" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 635.6 98.86 L 628.6 102.36 L 630.35 98.86 L 628.6 95.36 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 99px; margin-left: 576px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
HTTP
</div>
</div>
</div>
</foreignObject>
<text x="576" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
HTTP
</text>
</switch>
</g>
<rect x="384.86" y="66.71" width="128.86" height="64.29" rx="9.64" ry="9.64" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 127px; height: 1px; padding-top: 99px; margin-left: 386px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
mealie-frontend
</div>
</div>
</div>
</foreignObject>
<text x="449" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
mealie-frontend
</text>
</switch>
</g>
<path d="M 660.14 229.57 C 660.14 221.29 678.5 214.57 701.14 214.57 C 712.02 214.57 722.45 216.15 730.13 218.96 C 737.82 221.78 742.14 225.59 742.14 229.57 L 742.14 315.29 C 742.14 323.57 723.79 330.29 701.14 330.29 C 678.5 330.29 660.14 323.57 660.14 315.29 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 742.14 229.57 C 742.14 237.86 723.79 244.57 701.14 244.57 C 678.5 244.57 660.14 237.86 660.14 229.57" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 80px; height: 1px; padding-top: 285px; margin-left: 661px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
mealie-data
</div>
</div>
</div>
</foreignObject>
<text x="701" y="289" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
mealie-data
</text>
</switch>
</g>
<path d="M 701.05 131 L 701.05 162.79 Q 701.05 172.79 701.05 182.79 L 701.05 208.2" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 701.05 213.45 L 697.55 206.45 L 701.05 208.2 L 704.55 206.45 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 173px; margin-left: 701px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
/app/data
</div>
</div>
</div>
</foreignObject>
<text x="701" y="176" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
/app/data
</text>
</switch>
</g>
<path d="M 449.29 131 L 449.29 262.43 Q 449.29 272.43 459.29 272.43 L 653.77 272.43" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 659.02 272.43 L 652.02 275.93 L 653.77 272.43 L 652.02 268.93 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 272px; margin-left: 484px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
/app/data/
</div>
</div>
</div>
</foreignObject>
<text x="484" y="276" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
/app/data/
</text>
</switch>
</g>
<rect x="29" y="82.79" width="90.57" height="32.14" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 89px; height: 1px; padding-top: 99px; margin-left: 30px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Client
</div>
</div>
</div>
</foreignObject>
<text x="74" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Client
</text>
</switch>
</g>
<path d="M 320.43 98.86 L 342.48 98.86 Q 352.48 98.86 362.48 98.86 L 378.49 98.86" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 383.74 98.86 L 376.74 102.36 L 378.49 98.86 L 376.74 95.36 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<rect x="203.29" y="86" width="117.14" height="25.71" rx="3.86" ry="3.86" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 115px; height: 1px; padding-top: 99px; margin-left: 204px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
9925:3000
</div>
</div>
</div>
</foreignObject>
<text x="262" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
9925:3000
</text>
</switch>
</g>
<path d="M 119.57 98.86 L 151.05 98.86 Q 161.05 98.86 171.05 98.86 L 196.92 98.86" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 202.17 98.86 L 195.17 102.36 L 196.92 98.86 L 195.17 95.36 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 99px; margin-left: 161px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
HTTP
</div>
</div>
</div>
</foreignObject>
<text x="161" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
HTTP
</text>
</switch>
</g>
<rect x="297" y="60.29" width="70.29" height="38.57" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 80px; margin-left: 298px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
(1)
</div>
</div>
</div>
</foreignObject>
<text x="332" y="83" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold">
(1)
</text>
</switch>
</g>
<rect x="566.43" y="60.29" width="70.29" height="38.57" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 80px; margin-left: 567px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
(2)
</div>
</div>
</div>
</foreignObject>
<text x="602" y="83" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold">
(2)
</text>
</switch>
</g>
<rect x="497.31" y="239" width="70.29" height="38.57" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 258px; margin-left: 498px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
(3)
</div>
</div>
</div>
</foreignObject>
<text x="532" y="262" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold">
(3)
</text>
</switch>
</g>
<rect x="704.66" y="142.57" width="70.29" height="38.57" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 162px; margin-left: 706px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
(3)
</div>
</div>
</div>
</foreignObject>
<text x="740" y="165" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold">
(3)
</text>
</switch>
</g>
<rect x="384.86" y="44" width="70.29" height="28.57" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 58px; margin-left: 386px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
Container
</div>
</div>
</div>
</foreignObject>
<text x="420" y="62" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold">
Container
</text>
</switch>
</g>
<rect x="636.71" y="44" width="70.29" height="28.57" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 58px; margin-left: 638px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
Container
</div>
</div>
</div>
</foreignObject>
<text x="672" y="62" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold">
Container
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Viewer does not support full SVG 1.1
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 KiB

0
docs/docs/assets/img/home_screenshot.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 533 KiB

After

Width:  |  Height:  |  Size: 533 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 230 90" width="2500" height="978"><path d="M93.8 27.8l5.8-1.4v28c0 3.1.9 4.9 2.7 5.5-.9 1.7-2.4 2.6-4.6 2.6-2.6 0-4-1.8-4-5.5V27.8zM108.4 62V41.8h-3.2V37h9.1v25h-5.9zm3-34.6c.9 0 1.7.3 2.4 1s1 1.5 1 2.4c0 .9-.3 1.7-1 2.4s-1.5 1-2.4 1c-.9 0-1.7-.3-2.4-1s-1-1.5-1-2.4c0-.9.3-1.7 1-2.4s1.5-1 2.4-1zM137.1 62V47.6c0-2.1-.4-3.7-1.2-4.6-.8-1-2.1-1.5-4-1.5-.9 0-1.8.2-2.7.7-1 .5-1.7 1.1-2.3 1.8v18h-5.8V37.1h4.2l1.1 2.3c1.6-1.9 3.9-2.8 7-2.8 3 0 5.3.9 7 2.7 1.7 1.8 2.6 4.3 2.6 7.4V62h-5.9zM147.5 49.5c0-3.8 1.1-6.9 3.3-9.3 2.2-2.4 5.1-3.6 8.7-3.6 3.8 0 6.7 1.1 8.8 3.4 2.1 2.3 3.1 5.4 3.1 9.4s-1.1 7.1-3.2 9.5c-2.1 2.3-5 3.5-8.8 3.5-3.8 0-6.7-1.2-8.8-3.5-2-2.4-3.1-5.5-3.1-9.4zm6.1 0c0 5.5 2 8.2 5.9 8.2 1.8 0 3.2-.7 4.3-2.1 1.1-1.4 1.6-3.5 1.6-6.1 0-5.4-2-8.1-5.9-8.1-1.8 0-3.3.7-4.3 2.1-1.1 1.4-1.6 3.4-1.6 6zM192.1 62v-1.5c-.5.5-1.3 1-2.4 1.4-1.1.4-2.3.6-3.6.6-3.5 0-6.2-1.1-8.2-3.3-2-2.2-3-5.3-3-9.2 0-3.9 1.1-7.1 3.4-9.6s5.1-3.7 8.6-3.7c1.9 0 3.6.4 5.2 1.2v-10l5.8-1.4V62h-5.8zm0-19c-1.2-1-2.5-1.5-3.9-1.5-2.3 0-4.1.7-5.4 2.1-1.3 1.4-1.9 3.5-1.9 6.1 0 5.2 2.5 7.8 7.5 7.8.6 0 1.2-.2 2.1-.5.8-.3 1.3-.7 1.6-1V43zM226 51.3h-17.8c.1 2 .8 3.5 2 4.6 1.3 1.1 2.9 1.7 5.1 1.7 2.6 0 4.7-.7 6-2.1l2.3 4.4c-2 1.7-5.1 2.5-9.2 2.5-3.8 0-6.8-1.1-9-3.3-2.2-2.2-3.3-5.3-3.3-9.3 0-3.9 1.2-7.1 3.6-9.5 2.4-2.4 5.3-3.6 8.7-3.6 3.6 0 6.5 1.1 8.7 3.2 2.2 2.2 3.3 4.9 3.3 8.2.1.7-.1 1.7-.4 3.2zm-17.6-4.4h12.2c-.4-3.6-2.4-5.5-6-5.5-3.3.1-5.4 1.9-6.2 5.5z"/><g><path fill="#004712" d="M65.9 47.4l-1 11.5-3.3-2.3.4-5.8v-.1-.1l-.1-.1-.1-.1-7.1-4.7.1-5.1 11.1 6.8zM48.5 59.9L43.4 56v.9c0 .2-.1.4-.2.5L39.4 60l4.2 3.4.1.1v.2l.2 4 4.7 3.9-.1-11.7zm-32.1 5l2.4 11.5 9.9 10.5L27 75.3 16.4 64.9zm9.3 1.7l-2.4-16.1-12-10 3.2 15.6 11.2 10.5zm-3.8-26l-3.3-22.8L4.8 9.2l4.5 21.5 12.6 9.9z"/><path fill="#00B259" d="M75.7 41.2l-1.5 10.9-8.2 6.6 1-11.2 8.7-6.3zM49.6 59.9l.1 11.8 10.5-8.4.7-11.5-11.3 8.1zm-6.8 4.8L28 75.3l1.8 12.2 13.4-10.7-.4-12.1zm-.4-8l-.7-16-17.3 9.9 2.4 16.6 15.6-10.5zm-1.1-25.3l-.9-21.6-20.8 8L23 41l18.3-9.6z"/><path d="M76.9 40c0-.1 0-.1 0 0v-.2s0-.1-.1-.1c0 0-.1 0-.1-.1l-12-6.7c-.2-.1-.4-.1-.5 0L54 39.1h-.1v.6l-.1 5.4-4.1-2.7c-.2-.1-.4-.1-.6 0L43 45.8l-.3-6v-.1-.1-.1-.1-.1h-.1l-6.2-4.1 5.8-3c.2-.1.3-.3.3-.5L41.4 9v-.1s0-.1-.1-.1c0 0 0-.1-.1-.1L25.5 1.1c-.1-.1-.2-.1-.3-.1L3.9 7.6s-.1 0-.1.1c0 0-.1 0-.1.1v.6l4.7 22.9c0 .1.1.2.2.3l6.4 5-4.7 2.2s-.1 0-.1.1c0 0 0 .1-.1.1v.2l3.6 17.2c0 .1.1.2.2.3l4.5 4.2-3 1.8-.1.1s0 .1-.1.1V63.2L18 76.5c0 .1.1.2.1.3l10.9 12h.1s.1 0 .1.1h.5l14.4-11.5c.1-.1.2-.3.2-.4l-.3-7.9 4.8 4s.1 0 .1.1h.5L61 64c.1-.1.2-.2.2-.4l.4-5.8 3.5 2.4h.4s.1 0 .1-.1l9.4-7.5c.1-.1.2-.2.2-.3L76.9 40c0 .1 0 .1 0 0zM66 58.7l1-11.2 8.8-6.3-1.5 10.9-8.3 6.6zm-4.4-2.1l.4-5.8v-.1-.1l-.1-.1-.1-.1-7.1-4.7.1-5.1 11.1 6.9-1 11.5-3.3-2.4zm-1.5 6.7l-10.5 8.4-.1-11.8 11.3-8.1-.7 11.5zM43.3 76.8L29.8 87.5 28 75.3l14.7-10.5.6 12zm-24.6-.4l-2.4-11.5L27 75.3l1.7 11.6-10-10.5zm-.2-58.6l3.3 22.8-12.5-9.9L4.8 9.2l13.7 8.6zm21.9-8l.9 21.6L23 41l-3.4-23.2 20.8-8zm2 46.9L26.8 67.1l-2.4-16.6 17.3-9.9.7 16.1zm-19.1-6.1l2.4 16.1-11.2-10.6-3.2-15.6 12 10.1zm20.5 13.1v-.1-.1l-.1-.1-4.2-3.4 3.8-2.6c.2-.1.2-.3.2-.5V56l5.1 3.9.1 11.8-4.7-3.9-.2-4.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -51,3 +51,7 @@ a[target="_blank"]:not([class*="md-"]):after {
th {
font-weight: bold;
}
.md-button {
padding: 0.2rem 0.75rem !important;
}

View File

@@ -1,30 +0,0 @@
# vx.x.x COOL TITLE GOES HERE
**App Version: vx.x.x**
**Database Version: vx.x.x**
## Breaking Changes
!!! error "Breaking Changes"
#### Database
#### ENV Variables
## Bug Fixes
- Fixed ...
## Features and Improvements
### General
- New Thing 1
### UI Improvements
-
### Behind the Scenes
- Refactoring...

Some files were not shown because too many files have changed in this diff Show More