Compare commits

..

206 Commits

Author SHA1 Message Date
Hayden
ee121a12f8 New Crowdin updates (#1769)
* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)
2022-10-25 09:28:46 -08:00
Hayden
5a41e1d02d update workflow to properly grab tag 2022-10-25 09:27:30 -08:00
Hayden
3f5aab6fae chore: bump version + change-log notice (#1766) 2022-10-24 18:13:27 -08:00
Hayden
84c23765cd fix: strict optional errors (#1759)
* fix strict optional errors

* fix typing in repository

* fix backup db files location

* update workspace settings
2022-10-23 13:04:04 -08:00
Hayden
97d9e2a109 chore: lint-and-fix-vue-demi-imports (#1762)
* lint for 'vue-demi' imports

* fix invalid imports
2022-10-23 13:02:56 -08:00
Akash Kundu
46161ef12b docs(update): Fixed grammatical errors and typos (#1748)
A few grammatical errors and typos were fixed. @hay-kot Please review.
2022-10-23 12:25:22 -08:00
Hayden
d6e8602db5 New Crowdin updates (#1760)
* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)
2022-10-23 11:31:02 -08:00
Hayden
83a076bd8b feat: improve idle memory usage (#1758)
* health check as python script

* install crfpp model via python

* drop curl from finale container

* use uvicorn by default w/ gunicorn as opt in

* recommend setting mem limit for container
2022-10-22 20:00:13 -08:00
Hayden
c5613694d9 fix: display food label if no label present (#1757)
The only label that was applied to the shopping list view was one that was manually assigned. Now we first check if the item has a label, if not we check if the food has a label, then if there really is no label we display nothing.
2022-10-22 13:00:10 -08:00
Hayden
2e11e57e0a feat: fix recipe rating overwriting (#1756)
Previously, the recipe-ratings component would not sync to the v-modeled value when doing it's own updating. This PR fixes that issue and ensures that the value is pushed up to the parent whether in emit only mode or not.
2022-10-22 12:54:47 -08:00
Hayden
ce4315f971 chore: drop legacy editor (#1755)
* drop legacy editor

* remove unused components
2022-10-22 12:49:59 -08:00
Hayden
fcc5d99d40 chore: frontend testing setup (#1739)
* add vitest

* initialize lib w/ tests

* move to dev dep

* run tests in CI

* update file names

* move api folder to lib

* move api and api types to same folder

* update generator outpath

* rm husky

* i guess i _did_ need those types

* reorg types

* extract validators into testable components

* (WIP) start composable testing

* fix import type

* fix linter complaint

* simplify icon type def

* fix linter errors (maybe?)

* rename client file for sorting
2022-10-22 11:51:07 -08:00
Hayden
9f6bcc83d5 fix: apply API_PORT in containers (#1754) 2022-10-22 11:43:03 -08:00
Hayden
0d953da5b0 fix: require python-dateutil in dependencies (#1752) 2022-10-22 10:44:32 -08:00
Hayden
a56bedb022 fix: mis-ordered shoppinglist after checking item (#1749) 2022-10-21 20:35:45 -08:00
Hayden
558789cd02 fix firefox drag and drop + add visual indicator (#1747) 2022-10-21 20:01:08 -08:00
dependabot[bot]
bc99884438 fix(deps): bump vuetify from 2.6.11 to 2.6.12 in /frontend (#1740)
Bumps [vuetify](https://github.com/vuetifyjs/vuetify/tree/HEAD/packages/vuetify) from 2.6.11 to 2.6.12.
- [Release notes](https://github.com/vuetifyjs/vuetify/releases)
- [Commits](https://github.com/vuetifyjs/vuetify/commits/v2.6.12/packages/vuetify)

---
updated-dependencies:
- dependency-name: vuetify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-21 19:14:27 -08:00
Hayden
0bb1b6500f refactor: implement email with stdlib and drop email dependency (#1746)
* implement email with stdlib and drop dependency

* potentially provide in logs
2022-10-21 19:12:27 -08:00
Hayden
d53e78317d fix: advanced search now saved in URL (#1745)
* use query param for toggle state

* close #1678
2022-10-21 17:34:13 -08:00
Hayden
c9929745f8 fix: properly check recipe settings using database value (#1744)
* fix #1742

* bump depts to bust cache

* revert regression

* ensure permission checked on delete
2022-10-21 16:48:31 -08:00
Miroito
a59d1ae2f9 fix: min-original not saved (#1716) 2022-10-21 16:04:51 -08:00
Hayden
9ecef4c25f chore: file generation cleanup (#1736)
This PR does too many things :( 

1. Major refactoring of the dev/scripts and dev/code-generation folders. 

Primarily this was removing duplicate code and cleaning up some poorly written code snippets as well as making them more idempotent so then can be re-run over and over again but still maintain the same results. This is working on my machine, but I've been having problems in CI and comparing diffs so running generators in CI will have to wait. 

2. Re-Implement using the generated api routes for testing

This was a _huge_ refactor that touched damn near every test file but now we have auto-generated typed routes with inline hints and it's used for nearly every test excluding a few that use classes for better parameterization. This should greatly reduce errors when writing new tests. 

3. Minor Perf improvements for the All Recipes endpoint

  A. Removed redundant loops
  B. Uses orjson to do the encoding directly and returns a byte response instead of relying on the default 
       jsonable_encoder.

4. Fix some TS type errors that cropped up for seemingly no reason half way through the PR.

See this issue https://github.com/phillipdupuis/pydantic-to-typescript/issues/28

Basically, the generated TS type is not-correct since Pydantic will automatically fill in null fields. The resulting TS type is generated with a ? to indicate it can be null even though we _know_ that i can't be.
2022-10-18 14:49:41 -08:00
Hayden
a8f0fb14a7 chore: upgrade pre-commit hooks (#1735)
* change pep585 hook to pyupgrade

* run pyupgrade + cleanup lint errors
2022-10-17 14:37:06 -08:00
Hayden
e516a2e801 fix: unclosed sessions (#1734)
* resolve session leak

* cleanup session management functions
2022-10-17 14:11:40 -08:00
dependabot[bot]
a3904c45d8 fix(deps): bump @vueuse/core from 9.3.0 to 9.3.1 in /frontend (#1732)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 9.3.0 to 9.3.1.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v9.3.1/packages/core)

---
updated-dependencies:
- dependency-name: "@vueuse/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-17 13:37:55 -08:00
dependabot[bot]
61ba16ad26 chore(deps-dev): bump @vue/runtime-dom in /frontend (#1731)
Bumps [@vue/runtime-dom](https://github.com/vuejs/core/tree/HEAD/packages/runtime-dom) from 3.2.40 to 3.2.41.
- [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.41/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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-17 13:37:47 -08:00
Hayden
2914553084 New translations en-US.json (French) (#1717) 2022-10-15 12:36:51 -08:00
dependabot[bot]
1062ad9d97 chore(deps-dev): bump eslint from 8.24.0 to 8.25.0 in /frontend (#1718)
Bumps [eslint](https://github.com/eslint/eslint) from 8.24.0 to 8.25.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.24.0...v8.25.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-15 12:36:40 -08:00
dependabot[bot]
ed7849d92d fix(deps): bump isomorphic-dompurify from 0.22.0 to 0.23.0 in /frontend (#1708)
Bumps [isomorphic-dompurify](https://github.com/kkomelin/isomorphic-dompurify) from 0.22.0 to 0.23.0.
- [Release notes](https://github.com/kkomelin/isomorphic-dompurify/releases)
- [Commits](https://github.com/kkomelin/isomorphic-dompurify/compare/v0.22.0...v0.23.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-08 12:46:48 -08:00
dependabot[bot]
07b2692927 fix(deps): bump vuetify from 2.6.10 to 2.6.11 in /frontend (#1702)
Bumps [vuetify](https://github.com/vuetifyjs/vuetify/tree/HEAD/packages/vuetify) from 2.6.10 to 2.6.11.
- [Release notes](https://github.com/vuetifyjs/vuetify/releases)
- [Commits](https://github.com/vuetifyjs/vuetify/commits/v2.6.11/packages/vuetify)

---
updated-dependencies:
- dependency-name: vuetify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-08 12:46:30 -08:00
dependabot[bot]
03be764ce7 chore(deps-dev): bump eslint-plugin-vue from 9.5.1 to 9.6.0 in /frontend (#1701)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.5.1 to 9.6.0.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.5.1...v9.6.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-vue
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-08 12:46:14 -08:00
Hayden
5cfa276475 New Crowdin updates (#1712)
* New translations en-US.json (French)

* New translations en-US.json (French)
2022-10-08 12:45:47 -08:00
dependabot[bot]
f5440652a8 fix(deps): bump core-js from 3.25.4 to 3.25.5 in /frontend (#1699)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.25.4 to 3.25.5.
- [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.25.4...v3.25.5)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-03 19:27:51 -08:00
Michael Genson
e088719ddc fix: default null recipe quantities to zero when adding to shopping list (#1677)
* default null recipe quantities to zero

* simplify validation

* remove unused import

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-10-03 08:23:01 -08:00
dependabot[bot]
332870f608 fix(deps): bump @vueuse/core from 9.2.0 to 9.3.0 in /frontend (#1679)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 9.2.0 to 9.3.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v9.3.0/packages/core)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-03 08:13:52 -08:00
dependabot[bot]
8280099dc5 chore(deps-dev): bump @vue/runtime-dom in /frontend (#1686)
Bumps [@vue/runtime-dom](https://github.com/vuejs/core/tree/HEAD/packages/runtime-dom) from 3.2.39 to 3.2.40.
- [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.40/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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-02 18:15:29 -08:00
dependabot[bot]
52e07e4f21 fix(deps): bump core-js from 3.25.3 to 3.25.4 in /frontend (#1696)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.25.3 to 3.25.4.
- [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.25.3...v3.25.4)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-02 18:15:16 -08:00
Hayden
a3a66055e0 chore: update poetry install to match current docs (#1695) 2022-10-02 16:47:10 -08:00
Patrick
eca54e6474 rev poetry version (#1664) 2022-10-02 16:33:49 -08:00
Hayden
6159b55be9 fix: image and CI errors (#1694)
* bump cache version

* bump version

* delete template file

* maybe fix invalid CI?

* chain status command

* fix add tesseract bin to container & bump poetry (#1676)

* Add tesseract bin to container

* Add tesseract bin

Co-authored-by: Miroito <88556823+Miroito@users.noreply.github.com>
2022-10-02 16:24:13 -08:00
Hayden
47e7783ef5 New Crowdin updates (#1683)
* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Catalan)

* New translations en-US.json (Danish)
2022-10-02 16:18:09 -08:00
dependabot[bot]
2c04393501 fix(deps): bump core-js from 3.25.2 to 3.25.3 in /frontend (#1675)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.25.2 to 3.25.3.
- [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.25.2...v3.25.3)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-02 16:15:52 -08:00
dependabot[bot]
7aa7945647 chore(deps-dev): bump eslint from 8.23.1 to 8.24.0 in /frontend (#1673)
Bumps [eslint](https://github.com/eslint/eslint) from 8.23.1 to 8.24.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.23.1...v8.24.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-02 16:15:40 -08:00
Hayden
ef481a6a9c New Crowdin updates (#1672)
* New translations en-US.json (Turkish)

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

* New translations en-US.json (Lithuanian)

* 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 (Slovenian)

* New translations en-US.json (Swedish)

* New translations en-US.json (Japanese)

* New translations en-US.json (Ukrainian)

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

* 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 (Korean)

* New translations en-US.json (Italian)

* New translations en-US.json (French)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Romanian)

* New translations en-US.json (Spanish)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Arabic)

* New translations en-US.json (Catalan)

* New translations en-US.json (Hungarian)

* 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 (Hebrew)

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

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (Turkish)

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

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

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)
2022-09-27 18:55:35 -08:00
Michael Genson
796e55b7d5 refactor: webhook events (#1661)
* refactored EventBusService to work outside FastAPI

* extended event models

* refactored webhooks to run through event bus

* added basic webhook test route

* changed get_all to page_all

* fixed incorrectly implemented Vue variables

* fixed broken webhook test

* changed factory from staticmethod to classmethod

* made query boundary definitions easier to read
2022-09-27 18:55:20 -08:00
Miroito
025f1bc603 feat: press enter to create a shopping list! (#1657)
* feat/press enter to create list

* Press enter to submit base dialogs
2022-09-27 18:54:28 -08:00
Michael Genson
8271c3001e feat: add support for API extras on shopping lists, shopping list items, and food data (#1619)
* added api extras to other tables
genericized api extras model from recipes
added extras column to ingredient foods
added extras column to shopping lists
added extras column to shopping list items

* updated alembic version test

* made mypy happy

* added TODO on test that does nothing

* added extras tests for lists, items, and foods

* added docs for new extras

* modified alembic versions to eliminate branching
2022-09-27 18:53:22 -08:00
Hayden
db70a210a2 fix concurrent builds 2022-09-25 16:10:18 -08:00
Hayden
1d204e9f48 backend: bump deps (#1671) 2022-09-25 16:01:25 -08:00
Michael Genson
d3da551dae feat: add profile update suggestion to members page (#1669) 2022-09-25 15:19:26 -08:00
Hayden
124ec3743a New Crowdin updates (#1667)
* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Dutch)
2022-09-25 15:17:56 -08:00
Ben Boeckel
2e6b877ba9 docs: fix typos (#1665)
* docs: fix typos

* typos: fix typos found by `codespell` across the codebase

* docs: fix `macOS` spelling

* docs: fix `authentification` terminology

"Authentification" is not a thing.

* docs: fix `localhost` typo in example link

* typos: fix in-code typos

These are potentially higher risk, but no other mentions of these typos
show up in the codebase.
2022-09-25 15:17:27 -08:00
dependabot[bot]
33dad80eff chore(deps-dev): bump @types/sortablejs in /frontend (#1655)
Bumps [@types/sortablejs](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sortablejs) from 1.13.0 to 1.15.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-25 15:02:59 -08:00
dependabot[bot]
77622e0257 fix(deps): bump core-js from 3.25.1 to 3.25.2 in /frontend (#1654)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.25.1 to 3.25.2.
- [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.25.1...v3.25.2)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-25 15:02:35 -08:00
dependabot[bot]
6f301276a7 chore(deps-dev): bump eslint-plugin-vue from 9.4.0 to 9.5.1 in /frontend (#1653)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.4.0 to 9.5.1.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.4.0...v9.5.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-vue
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-25 15:02:23 -08:00
Hayden
39adea4ee3 feat (WIP): bring png OCR scanning support (#1670)
* Add pytesseract

* Add simple ocr endpoint

replace extension argument

* feat/ocr-editor gui

* fix frontend linting issues

* Add service unit tests

* Add split text modes & single ingredient/instruction editing

* make split mode really reactive

* Remove default step and ingredient

* make the linter haappy

* Accept only image uploads

* Add automatic recipe title suggestion

* Correct regex

* fix incorrect array.map method usage

* make the linter happy again

* Swap route to use asset name

* Rearange buttons

* fix test data

* feat: Allow making image the recipe image

* Add translation

* Make the linter happy

* Restrict function setPropertyValueByPath generic

* Restrict template literal type

* Add a more friendly icon to creation page

* update poetry lock file

* Correct sloppy ocr classes

* Make MyPy happy

* Rewrite safer tests

* Add tesseract to backend test CI container dependencies

* Make canvas element a component global

* Remove unwanted spaces in selected text

* Add way to know if recipe was created with ocr

* Access to ocr-editor for ocr recipes

* Update Alembic revision

* Make the frontend build

* Fix scrolling offset bug

* Allow creation of recipes with custom settings

* Fix rebasing mistakes

* Add format_tsv_output test

* Exclude the tests data directory only

* Enforce camelCase for frontend functions

* Remove import of unused component

* Fix type and class initialization

* Add multi-language support

* Highlight words in mount

* Fix image ratio bug

* Better ocr creation page

* Revert awkward feature to scroll in Selection mode

* Rebasing alembic migrations sux

* Remove obsolete getShared function

* Add function docstring

* Move down ocr creation option

* Make toolbar icons more generic

* Show help at the bottom of the page

* move ocr types to own file

* Use template ref for the canvas

* Use i18n.tc to get strings directly

* Correct naming mistake

* Move Ocr editor to own directory

* Create Ocr Editor parts

* Safeguard recipe properties access

* Add loading frontend animation due to longer request time

* minor cleanup chores

Co-authored-by: Miroito <alban.vachette@gmail.com>
2022-09-25 15:00:45 -08:00
Hayden
a8f3922907 New Crowdin updates (#1652)
* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)
2022-09-21 18:53:44 -08:00
dependabot[bot]
0dab4012d2 chore(deps-dev): bump @babel/eslint-parser in /frontend (#1651)
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.18.9 to 7.19.1.
- [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.19.1/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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-16 14:58:13 -08:00
Elegant
11eeab1b51 feat: LDAP improvements (#1487)
* Use Base DN for LDAP and fetch user attrs

Requires that a Base DN be set for LDAP
Set `full_name` and `email` based on LDAP attributes when creating user

* Add support for secure LDAP

Allow insecure LDAP connection (disabled by default)
Use CA when connecting to secure LDAP server

* Added missing quotes to example

* Update security.py

* Update security.py formatting

* Update security.py

Switched to f-String formatting

* formatting

* Update test_security.py

Added at attributes for testing

* Update test_security.py

Modified tests for base DN

* Update test_security.py

Set proper base DN for testing

* Update test_security.py

Corrected testing for LDAP

* Update test_security.py

Defined base_dn

* Authenticated user not in base DN

Add check for when user can authenticate but is not in base DN

* Update test_security.py

LDAP user cannot exist as it is searched before it is created and the list returns False

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-09-15 19:33:36 -08:00
dependabot[bot]
21161321e4 chore(deps-dev): bump eslint from 8.23.0 to 8.23.1 in /frontend (#1642)
Bumps [eslint](https://github.com/eslint/eslint) from 8.23.0 to 8.23.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.23.0...v8.23.1)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-15 15:55:00 -08:00
dependabot[bot]
38900e6773 fix(deps): bump date-fns from 2.29.2 to 2.29.3 in /frontend (#1648)
Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.29.2 to 2.29.3.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Changelog](https://github.com/date-fns/date-fns/blob/v2.29.3/CHANGELOG.md)
- [Commits](https://github.com/date-fns/date-fns/compare/v2.29.2...v2.29.3)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-15 15:54:42 -08:00
dependabot[bot]
fad02757d9 fix(deps): bump vuetify from 2.6.9 to 2.6.10 in /frontend (#1643)
Bumps [vuetify](https://github.com/vuetifyjs/vuetify/tree/HEAD/packages/vuetify) from 2.6.9 to 2.6.10.
- [Release notes](https://github.com/vuetifyjs/vuetify/releases)
- [Commits](https://github.com/vuetifyjs/vuetify/commits/v2.6.10/packages/vuetify)

---
updated-dependencies:
- dependency-name: vuetify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-15 15:54:25 -08:00
Michael Genson
86d25691d2 fix: defaults recipe actions to sort by name, ascending (#1650) 2022-09-15 15:53:58 -08:00
Hayden
1488b75b65 New Crowdin updates (#1645)
* New translations en-US.json (French)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)
2022-09-15 15:53:39 -08:00
Hayden
b56b987f5a chore: fix demo links (#1639)
* remove matrix builds

* replace beta.mealie.io w/ demo.mealie.io
2022-09-12 13:07:51 -08:00
Hayden
5337eaf1aa revert changes 2022-09-11 10:14:42 -08:00
Hayden
0e7dc75557 remove deps 2022-09-11 10:06:15 -08:00
Hayden
24cf21677c troubleshoot deploy-demo workflow 2022-09-11 10:05:50 -08:00
Philipp Fischbeck
c3459d540b fix: properly set "advanced features" during user registration (#1638)
Previously, "advanced features" was per group, not per user. With this change, this is now properly submitted on user registration. The "seed data" setting is also per group.
2022-09-11 09:56:23 -08:00
Hayden
5105b13219 publish docs on release 2022-09-10 16:22:35 -08:00
Hayden
ed1146f5fb add pip to dependabot 2022-09-10 16:19:17 -08:00
Hayden
9731317082 integrate demo deploy into CI 2022-09-10 15:56:46 -08:00
Hayden
80639d6968 split builders into to jobs 2022-09-10 13:45:06 -08:00
Hayden
a5472d7274 remove version 2022-09-10 11:14:11 -08:00
Hayden
6d818fe5bc add secrets 2022-09-10 11:13:40 -08:00
Hayden
7231c842c0 use relative refs 2022-09-10 11:10:28 -08:00
Hayden
e989651336 chore: cleanup actions and allow reuse + pr template (#1637)
* cleanup actions and allow reuse

* add PR template
2022-09-10 10:58:02 -08:00
Mirko Jeličić
2df791b80b feat: add option to stay in edit mode after loading from URL. (#1524)
* Add option to stay in edit mode after loading from URL.

* Stay in Edit mode now default behaviour after scraping recipe from URL.

* Fix missing param error.

* Fix incorrect read of boolean variable.

* Fix stupid error due to not understanding Vue.

* minor style and bug fixes

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-09-10 10:21:57 -08:00
dependabot[bot]
c65c00f3a8 chore(deps-dev): bump @vue/runtime-dom in /frontend (#1633)
Bumps [@vue/runtime-dom](https://github.com/vuejs/core/tree/HEAD/packages/runtime-dom) from 3.2.37 to 3.2.39.
- [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.39/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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-10 10:21:20 -08:00
dependabot[bot]
87bd0dda6d fix(deps): bump core-js from 3.25.0 to 3.25.1 in /frontend (#1631)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.25.0 to 3.25.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.25.0...v3.25.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-10 10:20:58 -08:00
dependabot[bot]
5399761dfd chore(deps-dev): bump @nuxtjs/eslint-config-typescript in /frontend (#1624)
Bumps [@nuxtjs/eslint-config-typescript](https://github.com/nuxt/eslint-config) from 10.0.0 to 11.0.0.
- [Release notes](https://github.com/nuxt/eslint-config/releases)
- [Changelog](https://github.com/nuxt/eslint-config/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nuxt/eslint-config/compare/v10.0.0...v11.0.0)

---
updated-dependencies:
- dependency-name: "@nuxtjs/eslint-config-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-10 10:20:41 -08:00
Philipp Fischbeck
1c938cb835 fix: set meta description and image for shared recipes (#1635) 2022-09-10 09:29:21 -08:00
gurudc
25c40b8abf docs: type in introduction.md (#1636)
Typo
2022-09-10 09:28:24 -08:00
dependabot[bot]
eacab576b4 fix(deps): bump @vueuse/core from 9.1.1 to 9.2.0 in /frontend (#1628)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 9.1.1 to 9.2.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v9.2.0/packages/core)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-10 09:23:07 -08:00
dependabot[bot]
eb9975a392 chore(deps-dev): bump eslint-plugin-nuxt in /frontend (#1623)
Bumps [eslint-plugin-nuxt](https://github.com/nuxt/eslint-plugin-nuxt) from 3.2.0 to 4.0.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/v3.2.0...v4.0.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-10 09:22:04 -08:00
Hayden
5829ebec91 New Crowdin updates (#1618)
* New translations en-US.json (Czech)

* New translations en-US.json (Swedish)

* 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 (Slovenian)

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

* New translations en-US.json (Turkish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Ukrainian)

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

* 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 (Norwegian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Danish)

* New translations en-US.json (French)

* 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 (German)

* New translations en-US.json (Korean)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

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

* New translations en-US.json (German)

* New translations en-US.json (Swedish)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Czech)

* New translations en-US.json (Catalan)

* New translations en-US.json (Catalan)

* New translations en-US.json (Czech)

* 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 (Spanish)

* New translations en-US.json (Catalan)

* New translations en-US.json (Spanish)

* New translations en-US.json (Catalan)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Catalan)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Catalan)

* New translations en-US.json (Catalan)

* New translations en-US.json (Catalan)

* New translations en-US.json (Danish)

* New translations en-US.json (Catalan)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)
2022-09-10 09:14:22 -08:00
Michael Genson
2007bcfe28 feat: added "cookbook" filter to recipe pagination to serve frontend (#1609)
* added cookbook filter to recipe pagination

* fixed wrong filter var

* restored cookbook sorting

* reverted unnecessary var change
2022-09-10 08:59:30 -08:00
dependabot[bot]
d26cb570ba chore(deps-dev): bump eslint from 8.22.0 to 8.23.0 in /frontend (#1617)
Bumps [eslint](https://github.com/eslint/eslint) from 8.22.0 to 8.23.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.22.0...v8.23.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-28 20:12:40 -08:00
Hayden
18b2c92a76 feat: public recipe access (#1610)
* initial public explorer API endpoint

* public API endpoint

* cleanup recipe page

* wip: init explorer page

* use public URLs for shared recipes

* refactor private share tokens to use shared page
2022-08-28 20:08:33 -08:00
Hayden
9ea5e6584f perf: use HEAD method to determine best image (#1614) 2022-08-28 14:14:02 -08:00
Hayden
4a0fb56d18 fix: unsafe window access on recipe assets (#1613)
resolves 500 error on new recipes page
2022-08-28 13:54:32 -08:00
Hayden
789ab27eef New Crowdin updates (#1611)
* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Swedish)
2022-08-28 12:45:48 -08:00
Hayden
3d099ec7c6 chore: bump frontend dependencies (#1608) 2022-08-27 11:13:53 -08:00
Michael Genson
23c039b42d refactor: event bus refactor (#1574)
* refactored event dispatching
added EventDocumentType and EventOperation to Event
added event listeners to bulk recipe changes
overhauled shopping list item events to be more useful
modified shopping list item repo to return more information

* added internal documentation for event types

* renamed message_types.py to event_types.py

* added unique event id and fixed instantiation

* generalized event listeners and publishers
moved apprise publisher to new apprise event listener
fixed duplicate message bug with apprise publisher

* added JWT field for user-specified integration id

* removed obselete test notification route

* tuned up existing notification tests

* added dependency to get integration_id from jwt

* added base crud controller to facilitate events

* simplified event publishing

* temporarily fixed test notification
2022-08-27 10:52:45 -08:00
Hayden
caa9e03050 refactor: recipe-page (#1587)
Refactor recipe page to use break up the component and make it more usable across different pages. I've left the old route in as well in case there is some functional breaks, I plan to remove it before the official release once we've tested the new editor some more in production. For now there will just have to be some duplicate components and pages around.
2022-08-27 10:44:58 -08:00
Michael Genson
a8da1a7594 fix: removed sort menu from pages that don't support it (#1606) 2022-08-26 18:50:24 -08:00
Michael Genson
fd4776842e docs: fixed incorrectly-labeled env vars (#1604) 2022-08-26 18:48:10 -08:00
Hayden
31793693f2 New Crowdin updates (#1595)
* New translations en-US.json (Czech)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Swedish)

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

* New translations en-US.json (French, Canada)
2022-08-26 14:21:45 -08:00
Hayden
98936e4ce2 New Crowdin updates (#1590)
* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Czech)
2022-08-23 22:45:07 -08:00
dependabot[bot]
6859ba346c fix(deps): bump @vueuse/core from 9.1.0 to 9.1.1 in /frontend (#1591)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 9.1.0 to 9.1.1.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v9.1.1/packages/core)

---
updated-dependencies:
- dependency-name: "@vueuse/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 22:44:43 -08:00
dependabot[bot]
e0b34457b5 chore(deps-dev): bump eslint-plugin-vue from 9.3.0 to 9.4.0 in /frontend (#1592)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.3.0 to 9.4.0.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.3.0...v9.4.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-vue
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 22:44:19 -08:00
Hayden
a25eb2e1bb New translations en-US.json (German) (#1589) 2022-08-21 18:15:47 -08:00
Hayden
2865bcbb04 fix: missing user agent for scraper (#1586)
* set user agent for requests.get

* bump scraper version
2022-08-20 17:54:06 -08:00
Hayden
007b861ad6 docs: add docs on restoring to PSQL server (#1584)
* docs: add docs on restoring to PSQL server

* prevent iframe in netlify site
2022-08-20 11:53:48 -08:00
Hayden
74548e9152 fix(frontend): proper null check for notes (#1583)
Adds a proper check for the mealplan.recipe property in multiple places to resolve the bug described in #1571. In development the page would fail to render.
2022-08-20 11:21:57 -08:00
Michael Genson
aaeb162dd5 feat: unify recipe card sections (#1560)
* removed unused import

* moved categories/tags to new recipe card section

* nuked old frontend sort code
minor refactoring

* bug fixes

* added backend recipes filter for tools

* removed debug log

* removed unusued props

* fixed sort for recipes by tool

* added tests for getting recipes by tool
2022-08-20 10:59:49 -08:00
Hayden
85448b8a18 New Crowdin updates (#1581)
* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)
2022-08-20 10:46:43 -08:00
Hayden
5c24949e19 New translations en-US.json (German) (#1579) 2022-08-19 23:02:05 -08:00
Michael Genson
f231109194 fix: print view for recipes with multiple ingredient sections (#1580)
* fix for recipes with multiple ingredient sections

* removed unused key
2022-08-19 23:01:55 -08:00
Hayden
692d91e338 New Crowdin updates (#1566)
* New translations en-US.json (Czech)

* New translations en-US.json (Swedish)

* 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 (Slovenian)

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

* New translations en-US.json (Turkish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Ukrainian)

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

* 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 (Norwegian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Danish)

* New translations en-US.json (French)

* 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 (German)

* New translations en-US.json (Korean)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

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

* 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 (Danish)

* 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)

* New translations en-US.json (Arabic)

* New translations en-US.json (German)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (German)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (French)
2022-08-19 11:43:31 -08:00
Michael Genson
3c2497bccf dev: improve container setup and pre-configure linters (#1577)
* added more formatters/linters

* moved "make setup" to onCreateCommand
2022-08-19 11:43:06 -08:00
Michael Genson
f4278737fb ui: changed ingredient auto grid to be more natural (#1578) 2022-08-19 11:42:06 -08:00
dependabot[bot]
b092654b43 fix(deps): bump date-fns from 2.29.1 to 2.29.2 in /frontend (#1575)
Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.29.1 to 2.29.2.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Changelog](https://github.com/date-fns/date-fns/blob/v2.29.2/CHANGELOG.md)
- [Commits](https://github.com/date-fns/date-fns/compare/v2.29.1...v2.29.2)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-18 18:31:11 -08:00
Hayden
dc41da3c7f ui: improve UI for favorite link (#1567) 2022-08-15 14:30:55 -08:00
Hayden
6858152acb New Crowdin updates (#1562)
* 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 (Hebrew)

* New translations en-US.json (German)
2022-08-15 13:56:18 -08:00
Philipp Fischbeck
ba15006bb1 chore: make vue global components and layouts localizable (#1565) 2022-08-15 13:55:51 -08:00
dependabot[bot]
7af48d51be chore(deps-dev): bump eslint from 8.21.0 to 8.22.0 in /frontend (#1561)
Bumps [eslint](https://github.com/eslint/eslint) from 8.21.0 to 8.22.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.21.0...v8.22.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-14 17:08:12 -08:00
Hayden
ad9ede20a2 fix: remove Config.get_attr config and use defaults for tags (#1559) 2022-08-14 11:31:29 -08:00
Hayden
3985713cbd fix: user-feedback-on-schema-mismatch (#1558)
* validate schema version on restore

* show user error on backup failure
2022-08-14 11:06:35 -08:00
Hayden
7adcc86d03 feat: bulk recipe settings update (#1557)
* extract switches from menu component

* implement bulk updater for settings

* fix browser cache api calls issue

* add frontend for bulk settings modifications
2022-08-14 10:37:44 -08:00
Michael Genson
5cfff75dbe moved header out of "cook mode" conditional block (#1556) 2022-08-14 08:04:09 -08:00
Hayden
3b45da274c New Crowdin updates (#1553)
* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)
2022-08-13 21:44:27 -08:00
Michael Genson
238f555f5e fix: general bug fixes (#1547)
* grouped "instructions" header with first section

* fixed sort by last updated date

* somewhat-hacky bugfix for large screens

* modified page size to be divisible by 4

* fixed missing export for new data forms
2022-08-13 21:44:11 -08:00
Hayden
54c4f19a5c security: enforce min length for user password (#1555)
* fix typing on auth context

* extract user password strength meter

* fix broken useToggle method

* extend form to accept arguments for validators

* enforce password length on update

* fix user password change form
2022-08-13 21:38:26 -08:00
Hayden
b3c41a4bd0 security: implement user lockout (#1552)
* add data-types required for login security

* implement user lockout checking at login

* cleanup legacy patterns

* expose passwords in test_user

* test user lockout after bad attempts

* test user service

* bump alembic version

* save increment to database

* add locked_at to datetime transformer on import

* do proper test cleanup

* implement scheduled task

* spelling

* document env variables

* implement context manager for session

* use context manager

* implement reset script

* cleanup generator

* run generator

* implement API endpoint for resetting locked users

* add button to reset all locked users

* add info when account is locked

* use ignore instead of expect-error
2022-08-13 13:18:12 -08:00
Daniel Palstra
ca64584fd1 feat: add the RecipeCardMenu to the recipe in a mealplan (#1551)
* Add the RecipeCardMenu to the recipe in a mealplan

* fix parameter naming

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-08-12 09:02:08 -08:00
Bryce Willey
553325ed09 fix: use flex-basis, not width to fix min size bug (#1548)
On mobile screens when following a registration invite link, the page
would be too wide to interact with, extending well over the sides of the
phone.

This is because the minimum size of content is set to `auto` (by
default), and accord to the spec (https://www.w3.org/TR/css-flexbox-1/#specified-size-suggestion)
the minimum size of the element is the mimimum size of it's content. The
password strength element in the panel had a width of 500px, making the
entire component overflow the screen.

Changing the width to `flex-basis` instead, allows for the password
strength element to shrink if it overflows the screen.
2022-08-12 08:59:34 -08:00
Ryan Quinn
ff2334a489 fix: email typos (#1549)
* "your" -> "you're" in invitation email

* "below" -> "above" in all emails
2022-08-12 08:54:41 -08:00
Hayden
71d3db7aef refactor: move dependencies to controllers (#1550)
* Moves dependencies directly to controllers
* Reduces use of @cached_property - (I have a suspicion that this is a factor in memory usage)
* reduce duplicate ways to access the same property on a controller.
2022-08-11 20:13:22 -08:00
Hayden
8a98288248 New Crowdin updates (#1546)
* New translations en-US.json (Czech)

* New translations en-US.json (Swedish)

* 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 (Slovenian)

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

* New translations en-US.json (Turkish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Ukrainian)

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

* 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 (Norwegian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Danish)

* New translations en-US.json (French)

* 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 (German)

* New translations en-US.json (Korean)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

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

* New translations en-US.json (Dutch)
2022-08-10 16:03:01 -08:00
dependabot[bot]
03147c8723 fix(deps): bump vuetify from 2.6.8 to 2.6.9 in /frontend (#1543)
Bumps [vuetify](https://github.com/vuetifyjs/vuetify/tree/HEAD/packages/vuetify) from 2.6.8 to 2.6.9.
- [Release notes](https://github.com/vuetifyjs/vuetify/releases)
- [Commits](https://github.com/vuetifyjs/vuetify/commits/v2.6.9/packages/vuetify)

---
updated-dependencies:
- dependency-name: vuetify
  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-08-09 21:23:02 -08:00
Philipp Fischbeck
32244988d2 chore: make vue domain components localizable (#1532) 2022-08-09 21:12:45 -08:00
Hayden
e5bf7bce17 fix: case-sensitive-password-reset (#1545)
* make password reset case insensitive

* update test to check for case insensitive
2022-08-09 21:10:00 -08:00
Hayden
ef24705cfa fix: ignore-place-in-search (#1544)
* pin i18n since it breaks things

* set ignoreLocation to true

* revert composition api demotion

* bump slugify
2022-08-09 20:34:08 -08:00
Michael Genson
f45e2587a0 feat: category and tag filters to recipe pagination route (#1508)
* fixed incorrect response model

* added category and tag filters

* moved categories and tags params to route and
changed to query array param

* type fixes

* added category and tag tests
2022-08-09 14:01:59 -08:00
Hayden
e82e7d0fb3 New Crowdin updates (#1527)
* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)
2022-08-08 18:42:09 -08:00
Maka0
a7c6e89dfa feat: added gosu & updated run.sh to switch to the dedicated user (#1395)
Co-authored-by: Maka0 <360614-Maka0@users.noreply.gitlab.com>
2022-08-08 18:39:59 -08:00
Hayden
7ce02c31d5 chore: bump build deps (#1541)
* bump build deps

* typescript nonsense

* bump prettier
2022-08-08 18:38:18 -08:00
dependabot[bot]
95b1b8bbdc fix(deps): bump @vueuse/core from 9.0.2 to 9.1.0 in /frontend (#1535)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 9.0.2 to 9.1.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v9.1.0/packages/core)

---
updated-dependencies:
- dependency-name: "@vueuse/core"
  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-08-08 18:11:17 -08:00
dependabot[bot]
f5040af8bb chore(deps-dev): bump @babel/eslint-parser in /frontend (#1538)
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.18.2 to 7.18.9.
- [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.9/eslint/babel-eslint-parser)

---
updated-dependencies:
- dependency-name: "@babel/eslint-parser"
  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-08-08 18:11:03 -08:00
dependabot[bot]
5fd79457dd fix(deps): bump date-fns from 2.28.0 to 2.29.1 in /frontend (#1542)
Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.28.0 to 2.29.1.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Changelog](https://github.com/date-fns/date-fns/blob/main/CHANGELOG.md)
- [Commits](https://github.com/date-fns/date-fns/compare/v2.28.0...v2.29.1)

---
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-08-08 18:10:41 -08:00
dependabot[bot]
75d4d9658c fix(deps): bump vuetify from 2.6.6 to 2.6.8 in /frontend (#1540)
Bumps [vuetify](https://github.com/vuetifyjs/vuetify/tree/HEAD/packages/vuetify) from 2.6.6 to 2.6.8.
- [Release notes](https://github.com/vuetifyjs/vuetify/releases)
- [Commits](https://github.com/vuetifyjs/vuetify/commits/v2.6.8/packages/vuetify)

---
updated-dependencies:
- dependency-name: vuetify
  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-08-08 18:04:53 -08:00
dependabot[bot]
9907b9d625 fix(deps): bump @mdi/js from 6.7.96 to 7.0.96 in /frontend (#1539)
Bumps [@mdi/js](https://github.com/Templarian/MaterialDesign-JS) from 6.7.96 to 7.0.96.
- [Release notes](https://github.com/Templarian/MaterialDesign-JS/releases)
- [Commits](https://github.com/Templarian/MaterialDesign-JS/compare/v6.7.96...v7.0.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-08-08 18:04:44 -08:00
Hayden
60ecba2f92 chore: deps-dev-bump (#1533)
* bump dev dependencies

* fix/generate font sheets

* bump python deps to nuke cache
2022-08-08 17:58:10 -08:00
Philipp Fischbeck
34cd6eb687 fix: validate OpenAPI spec (#1528)
* init api check test

* Fix openAPI issues

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-08-06 16:54:29 -08:00
zierbeek
505e594758 fix (possible): PWA white screen (#1526)
Possible fix #1077 on iOS, as referenced in discussion. Should be enabled by default normally..

https://pwa.nuxtjs.org/workbox#skipwaiting
2022-08-04 10:42:41 -08:00
zierbeek
ea42350244 fix (possible): PWA blank screen (#1523)
Possible fix for blank screen on PWA. #1077

https://stackoverflow.com/questions/54928050/vue-pwa-blank-screen-after-closing-the-app
2022-08-02 14:06:26 -08:00
Hayden
11478134a1 security: restrict backup file upload (#1522) 2022-08-02 12:53:58 -08:00
Hayden
6649ccf224 lang: options updates + gen utils cleanup (#1520)
* generate new langs

* add to nuxt

* cleanup generator code

* additional cleanups
2022-08-02 10:41:44 -08:00
Hayden
5fca94dd45 New Crowdin updates (#1519)
* New translations en-US.json (French, Canada)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)
2022-08-02 10:29:45 -08:00
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
698 changed files with 25972 additions and 10742 deletions

View File

@@ -35,16 +35,22 @@
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"dbaeumer.vscode-eslint",
"matangover.mypy",
"ms-python.black-formatter",
"ms-python.isort",
"ms-python.python",
"ms-python.vscode-pylance"
"ms-python.vscode-pylance",
"Vue.volar"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
3000,
9000
],
// Use 'onCreateCommand' to run commands at the end of container creation.
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "sudo chown -R vscode:vscode /workspaces/mealie/frontend/node_modules && make setup",
"onCreateCommand": "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": {

View File

@@ -30,3 +30,17 @@ updates:
prefix: fix
prefix-development: chore
include: scope
- package-ecosystem: pip
directory: "/mealie"
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

72
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,72 @@
<!--
This template provides some ideas of things to include in your PR description.
To start, try providing a short summary of your changes in the Title above.
If a section of the PR template does not apply to this PR, then delete that section.
-->
## What type of PR is this?
_(REQUIRED)_
<!--
Delete any of the following that do not apply:
-->
- bug
- cleanup
- documentation
- feature
## What this PR does / why we need it:
_(REQUIRED)_
<!--
What goal is this change working towards?
Provide a bullet pointed summary of how each file was changed.
Briefly explain any decisions you made with respect to the changes.
Include anything here that you didn't include in *Release Notes*
above, such as changes to CI or changes to internal methods.
-->
## Which issue(s) this PR fixes:
_(REQUIRED)_
<!--
If this PR fixes one of more issues, list them here.
One line each, like so:
Fixes #123
Fixes #39
-->
## Special notes for your reviewer:
_(fill-in or delete this section)_
<!--
Is there any particular feedback you would / wouldn't like?
Which parts of the code should reviewers focus on?
-->
## Testing
_(fill-in or delete this section)_
<!--
Describe how you tested this change.
-->
## Release Notes
_(REQUIRED)_
<!--
If this PR makes user facing changes, please describe them here. This
description will be copied into the release notes/changelog, whenever the
next version is released. Keep this section short, and focus on high level
changes.
Put your text between the block. To omit notes, use NONE within the block.
-->
```release-note
```

View File

@@ -1,63 +0,0 @@
name: Backend - Nightly Build
on:
push:
branches:
- mealie-next
concurrency:
group: backend-nightly-${{ github.ref }}
cancel-in-progress: true
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 --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,75 +0,0 @@
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,63 +0,0 @@
name: Frontend - Nightly Build
on:
push:
branches:
- mealie-next
concurrency:
group: frontend-nightly-${{ github.ref }}
cancel-in-progress: true
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
working-directory: "frontend"
run: |
docker build --push --no-cache \
--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"

View File

@@ -1,49 +0,0 @@
name: Frontend Lint
on:
push:
branches:
- mealie-next
pull_request:
branches:
- mealie-next
jobs:
ci:
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"

64
.github/workflows/nightly.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Docker Nightly Production
on:
push:
branches:
- mealie-next
concurrency:
group: nightly-${{ github.ref }}
cancel-in-progress: true
jobs:
backend-tests:
name: "Backend Server Tests"
uses: ./.github/workflows/partial-backend.yml
frontend-tests:
name: "Frontend and End-to-End Tests"
uses: ./.github/workflows/partial-frontend.yml
build-release:
name: Build Tagged Release
uses: ./.github/workflows/partial-builder.yml
needs:
- frontend-tests
- backend-tests
with:
tag: nightly
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
notify-discord:
name: Notify Discord
needs:
- build-release
runs-on: ubuntu-latest
steps:
- 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 and mealie:frontend-nightly is available"
deploy-demo:
runs-on: ubuntu-latest
name: Deploy Demo
needs:
- build-release
steps:
- name: Clean and Deploy Demo
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.DEMO_SERVER_IP }}
username: ${{ secrets.DEMO_SERVER_USER }}
key: ${{ secrets.DEMO_SERVER_SSH_KEY }}
port: ${{ secrets.DEMO_SERVER_PORT }}
script_stop: true
script: |
cd ~/docker/mealie-next
docker-compose pull
docker-compose down -v
docker-compose up -d

View File

@@ -1,12 +1,7 @@
name: Backend Tests
name: Backend Test/Lint
on:
push:
branches:
- mealie-next
pull_request:
branches:
- mealie-next
types: [synchronize, opened, reopened, ready_for_review]
workflow_call:
jobs:
tests:
@@ -34,56 +29,56 @@ jobs:
- 5432:5432
# Steps
steps:
#----------------------------------------------
# check-out repo and set-up python
#----------------------------------------------
- name: Check out repository
uses: actions/checkout@v2
- name: Set up python
uses: actions/setup-python@v2
with:
python-version: "3.10"
#----------------------------------------------
# ----- install & configure poetry -----
#----------------------------------------------
- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
#----------------------------------------------
# load cached venv if cache exists
#----------------------------------------------
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: .venv
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
#----------------------------------------------
# install dependencies if cache does not exist
#----------------------------------------------
- name: Check venv cache
id: cache-validate
if: steps.cached-poetry-dependencies.outputs.cache-hit == 'true'
run: |
echo "print('venv good?')" > test.py && poetry run python test.py && echo ::set-output name=cache-hit-success::true
rm test.py
continue-on-error: true
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install libsasl2-dev libldap2-dev libssl-dev
sudo apt-get install libsasl2-dev libldap2-dev libssl-dev tesseract-ocr-all
poetry install
poetry add "psycopg2-binary==2.8.6"
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' || steps.cache-validate.outputs.cache-hit-success != 'true'
#----------------------------------------------
# run test suite
#----------------------------------------------
- name: Formatting (Black & isort)
run: |
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: ${{ matrix.Database }}

80
.github/workflows/partial-builder.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
name: Build Containers
on:
workflow_call:
inputs:
tag:
required: true
type: string
secrets:
DOCKERHUB_USERNAME:
required: true
DOCKERHUB_TOKEN:
required: true
jobs:
build-frontend:
runs-on: ubuntu-latest
name: Build Frontend
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
install: true
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build Frontend Image
working-directory: "frontend"
run: |
docker build --push --no-cache \
--tag hkotel/mealie:frontend-${{ inputs.tag }} \
--platform linux/amd64,linux/arm64 .
build-backend:
runs-on: ubuntu-latest
name: Build Backend
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
install: true
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build Backend Image
run: |
docker build --push --no-cache \
--tag hkotel/mealie:api-${{ inputs.tag }} \
--build-arg COMMIT=$(git rev-parse HEAD) \
--platform linux/amd64,linux/arm64 .

77
.github/workflows/partial-frontend.yml vendored Normal file
View File

@@ -0,0 +1,77 @@
name: Frontend Build/Lin
on:
workflow_call:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎
uses: actions/checkout@master
- name: Setup node env 🏗
uses: actions/setup-node@v2.1.5
with:
node-version: 16
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"
- name: Run tests 🧪
run: yarn test:ci
working-directory: "frontend"
build:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎
uses: actions/checkout@master
- name: Setup node env 🏗
uses: actions/setup-node@v2.1.5
with:
node-version: 16
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"

15
.github/workflows/pull-requests.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: PR CI
on:
pull_request:
branches:
- mealie-next
jobs:
backend-tests:
name: "Backend Server Tests"
uses: ./.github/workflows/partial-backend.yml
frontend-tests:
name: "Frontend and End-to-End Tests"
uses: ./.github/workflows/partial-frontend.yml

55
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Docker Build Production
on:
release:
types: [published]
jobs:
backend-tests:
name: "Backend Server Tests"
uses: ./.github/workflows/partial-backend.yml
frontend-tests:
name: "Frontend and End-to-End Tests"
uses: ./.github/workflows/partial-frontend.yml
build-release:
name: Build Tagged Release
uses: ./.github/workflows/partial-builder.yml
needs:
- backend-tests
- frontend-tests
with:
tag: ${{ github.event.release.tag_name }}
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
publish-docs:
name: Deploy docs
runs-on: ubuntu-latest
needs:
- build-release
steps:
- name: Checkout main
uses: actions/checkout@v2
- 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
notify-discord:
name: Notify Discord
needs:
- build-release
runs-on: ubuntu-latest
steps:
- 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 }}"

2
.gitignore vendored
View File

@@ -55,7 +55,6 @@ develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
!frontend/src/components/Recipe/Parts/
@@ -158,3 +157,4 @@ dev/code-generation/generated/openapi.json
dev/code-generation/generated/test_routes.py
mealie/services/parser_services/crfpp/model.crfmodel
lcov.info
dev/code-generation/openapi.json

View File

@@ -9,10 +9,11 @@ repos:
- 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
exclude: ^tests/data/
- repo: https://github.com/asottile/pyupgrade
rev: v3.1.0
hooks:
- id: upgrade-type-hints
- id: pyupgrade
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:

View File

@@ -1,10 +1,4 @@
{
"conventionalCommits.scopes": [
"frontend",
"docs",
"backend",
"code-generation"
],
"cSpell.enableFiletypes": ["!javascript", "!python", "!yaml"],
"cSpell.words": [
"chowdown",
@@ -44,7 +38,7 @@
"python.testing.unittestEnabled": false,
"python.analysis.typeCheckingMode": "off",
"python.linting.mypyEnabled": true,
"python.sortImports.path": "${workspaceFolder}/.venv/bin/isort",
"isort.path": ["${workspaceFolder}/.venv/bin/isort"],
"search.mode": "reuseEditor",
"python.testing.unittestArgs": ["-v", "-s", "./tests", "-p", "test_*.py"],
"explorer.fileNesting.enabled": true,

View File

@@ -34,14 +34,15 @@ RUN apt-get update \
build-essential \
libpq-dev \
libwebp-dev \
tesseract-ocr-all \
# LDAP Dependencies
libsasl2-dev libldap2-dev libssl-dev \
gnupg gnupg2 gnupg1 \
&& pip install -U --no-cache-dir pip
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
ENV POETRY_VERSION=1.1.6
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python -
ENV POETRY_VERSION=1.2.1
RUN curl -sSL https://install.python-poetry.org | python3 -
# copy project requirement files here to ensure they will be cached.
WORKDIR $PYSETUP_PATH
@@ -94,10 +95,10 @@ ENV TESTING=false
ARG COMMIT
ENV GIT_COMMIT_HASH=$COMMIT
# curl for used by healthcheck
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
curl \
gosu \
tesseract-ocr-all \
&& apt-get autoremove \
&& rm -rf /var/lib/apt/lists/*
@@ -105,9 +106,6 @@ RUN apt-get update \
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
# 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
@@ -128,14 +126,14 @@ RUN . $VENV_PATH/bin/activate && poetry install -E pgsql --no-dev
WORKDIR /
# Grab CRF++ Model Release
RUN curl -L0 $CRF_MODEL_URL --output $MEALIE_HOME/mealie/services/parser_services/crfpp/model.crfmodel
RUN python $MEALIE_HOME/mealie/scripts/install_model.py
VOLUME [ "$MEALIE_HOME/data/" ]
ENV APP_PORT=9000
EXPOSE ${APP_PORT}
HEALTHCHECK CMD curl -f http://localhost:${APP_PORT}/docs || exit 1
HEALTHCHECK CMD python $MEALIE_HOME/mealie/scripts/healthcheck.py || exit 1
RUN chmod +x $MEALIE_HOME/mealie/run.sh
ENTRYPOINT $MEALIE_HOME/mealie/run.sh

View File

@@ -42,18 +42,18 @@
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)!
- [Documentation](https://docs.mealie.io)
- [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**. 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.
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.
- 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.
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>
@@ -64,7 +64,7 @@ Distributed under the MIT License. See `LICENSE` for more information.
## 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.
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.
@@ -87,7 +87,7 @@ Thanks to Linode for providing Hosting for the Demo, Beta, and Documentation sit
[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

View File

@@ -23,8 +23,8 @@ def is_postgres():
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
# SQLite doesn't require migration as types are not inforced.
# Postgres Specifc Migration
# SQLite doesn't require migration as types are not enforced.
# Postgres Specific Migration
if is_postgres():
op.alter_column(
"recipes_ingredients",
@@ -38,8 +38,8 @@ def upgrade():
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
# SQLite doesn't require migration as types are not inforced.
# Postgres Specifc Migration
# SQLite doesn't require migration as types are not enforced.
# Postgres Specific Migration
if is_postgres():
op.alter_column(
"recipes_ingredients",

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

View File

@@ -0,0 +1,27 @@
"""Add is_ocr_recipe column to recipes
Revision ID: 089bfa50d0ed
Revises: f30cf048c228
Create Date: 2022-08-05 17:07:07.389271
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "089bfa50d0ed"
down_revision = "188374910655"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("recipes", sa.Column("is_ocr_recipe", sa.Boolean(), default=False, nullable=True))
op.execute("UPDATE recipes SET is_ocr_recipe = FALSE")
# SQLITE does not support ALTER COLUMN, so the column will stay nullable to prevent making this migration a mess
# The Recipe pydantic model and the SQL server use False as default value anyway for this column so Null should be a very rare sight
def downgrade():
op.drop_column("recipes", "is_ocr_recipe")

View File

@@ -0,0 +1,26 @@
"""add login_attemps and locked_at field to user table
Revision ID: 188374910655
Revises: f30cf048c228
Create Date: 2022-08-12 19:05:59.776361
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "188374910655"
down_revision = "f30cf048c228"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("users", sa.Column("login_attemps", sa.Integer(), nullable=True))
op.add_column("users", sa.Column("locked_at", sa.DateTime(), nullable=True))
def downgrade():
op.drop_column("users", "locked_at")
op.drop_column("users", "login_attemps")

View File

@@ -0,0 +1,72 @@
"""add extras to shopping lists, list items, and ingredient foods
Revision ID: 44e8d670719d
Revises: 188374910655
Create Date: 2022-08-29 13:57:40.452245
"""
import sqlalchemy as sa
import mealie.db.migration_types
from alembic import op
# revision identifiers, used by Alembic.
revision = "44e8d670719d"
down_revision = "089bfa50d0ed"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"shopping_list_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("key_name", sa.String(), nullable=True),
sa.Column("value", sa.String(), nullable=True),
sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["shopping_list_id"],
["shopping_lists.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"ingredient_food_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("key_name", sa.String(), nullable=True),
sa.Column("value", sa.String(), nullable=True),
sa.Column("ingredient_food_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["ingredient_food_id"],
["ingredient_foods.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"shopping_list_item_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("key_name", sa.String(), nullable=True),
sa.Column("value", sa.String(), nullable=True),
sa.Column("shopping_list_item_id", mealie.db.migration_types.GUID(), nullable=True),
sa.ForeignKeyConstraint(
["shopping_list_item_id"],
["shopping_list_items.id"],
),
sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("shopping_list_item_extras")
op.drop_table("ingredient_food_extras")
op.drop_table("shopping_list_extras")
# ### end Alembic commands ###

View File

@@ -2,8 +2,6 @@ preserve_hierarchy: false
files:
- source: /frontend/lang/messages/en-US.json
translation: /frontend/lang/messages/%locale%.json
- source: /frontend/lang/dateTimeFormats/en-US.json
translation: /frontend/lang/dateTimeFormats/%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

View File

@@ -1,26 +0,0 @@
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

@@ -1,39 +0,0 @@
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

@@ -1,15 +1,13 @@
from dataclasses import dataclass
from pathlib import Path
from _gen_utils import render_python_template
from slugify import slugify
from utils import render_python_template
CWD = Path(__file__).parent
TEMPLATE = CWD / "templates" / "test_data.py.j2"
TEST_DATA = CWD.parent.parent / "tests" / "data"
GENERATED = CWD / "generated"
@@ -27,9 +25,7 @@ class TestDataPath:
# Remove any file extension
var = var.split(".")[0]
var = var.replace("'", "")
var = slugify(var, separator="_")
return cls(var, rel_path)
@@ -99,8 +95,6 @@ def rename_non_compliant_paths():
def main():
print("Starting Template Generation")
rename_non_compliant_paths()
GENERATED.mkdir(exist_ok=True)
@@ -117,8 +111,6 @@ def main():
{"children": all_children},
)
print("Finished Template Generation")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,84 @@
import json
from pathlib import Path
from fastapi import FastAPI
from jinja2 import Template
from pydantic import BaseModel
from utils import PROJECT_DIR, CodeTemplates, HTTPRequest, RouteObject
CWD = Path(__file__).parent
OUTFILE = PROJECT_DIR / "tests" / "utils" / "api_routes" / "__init__.py"
class PathObject(BaseModel):
route_object: RouteObject
http_verbs: list[HTTPRequest]
class Config:
arbitrary_types_allowed = True
def get_path_objects(app: FastAPI):
paths = []
for key, value in app.openapi().items():
if key == "paths":
for key, value in value.items():
paths.append(
PathObject(
route_object=RouteObject(key),
http_verbs=[HTTPRequest(request_type=k, **v) for k, v in value.items()],
)
)
return paths
def 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) as f:
return f.read()
def generate_python_templates(static_paths: list[PathObject], function_paths: list[PathObject]):
template = Template(read_template(CodeTemplates.pytest_routes))
content = template.render(
paths={
"prefix": "/api",
"static_paths": static_paths,
"function_paths": function_paths,
}
)
with open(OUTFILE, "w") as f:
f.write(content)
return
def main():
from mealie.app import 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)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,102 @@
import pathlib
import re
from dataclasses import dataclass, field
from utils import PROJECT_DIR, log, render_python_template
template = """# This file is auto-generated by gen_schema_exports.py
{% for file in data.module.files %}{{ file.import_str() }}
{% endfor %}
__all__ = [
{% for file in data.module.files %}
{%- for class in file.classes -%}
"{{ class }}",
{%- endfor -%}
{%- endfor %}
]
"""
SCHEMA_PATH = PROJECT_DIR / "mealie" / "schema"
SKIP = {"static", "__pycache__"}
class PyFile:
import_path: str
"""The import path of the file"""
classes: list[str]
"""A list of classes in the file"""
def __init__(self, path: pathlib.Path):
self.import_path = path.stem
self.classes = []
self.classes = PyFile.extract_classes(path)
self.classes.sort()
def import_str(self) -> str:
"""Returns a string that can be used to import the file"""
return f"from .{self.import_path} import {', '.join(self.classes)}"
@staticmethod
def extract_classes(file_path: pathlib.Path) -> list[str]:
name = file_path.stem
if name == "__init__" or name.startswith("_"):
return []
classes = re.findall(r"(?m)^class\s(\w+)", file_path.read_text())
return classes
@dataclass
class Modules:
directory: pathlib.Path
"""The directory to search for modules"""
files: list[PyFile] = field(default_factory=list)
"""A list of files in the directory"""
def __post_init__(self):
for file in self.directory.glob("*.py"):
if file.name.startswith("_"):
continue
pfile = PyFile(file)
if len(pfile.classes) > 0:
self.files.append(pfile)
else:
log.debug(f"Skipping {file.name} as it has no classes")
def find_modules(root: pathlib.Path) -> list[Modules]:
"""Finds all the top level modules in the provided folder"""
modules: list[Modules] = []
for file in root.iterdir():
if file.is_dir() and file.name not in SKIP:
modules.append(Modules(directory=file))
return modules
def main():
modules = find_modules(SCHEMA_PATH)
for module in modules:
log.debug(f"Module: {module.directory.name}")
for file in module.files:
log.debug(f" File: {file.import_path}")
log.debug(f" Classes: [{', '.join(file.classes)}]")
render_python_template(template, module.directory / "__init__.py", {"module": module})
if __name__ == "__main__":
main()

View File

@@ -1,53 +0,0 @@
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

@@ -1,35 +0,0 @@
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

@@ -1,11 +1,12 @@
import pathlib
from pathlib import Path
import _static
import dotenv
import requests
from jinja2 import Template
from pydantic import Extra
from requests import Response
from rich import print
from utils import CodeDest, CodeKeys, inject_inline, log
from mealie.schema._mealie import MealieModel
@@ -13,6 +14,7 @@ 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",
@@ -35,7 +37,7 @@ NAMES = {
"nl-NL": "Nederlands (Dutch)",
"pl-PL": "Polski (Polish)",
"pt-BR": "Português do Brasil (Brazilian Portuguese)",
"pt-PT": "Português (Portugese)",
"pt-PT": "Português (Portuguese)",
"ro-RO": "Română (Romanian)",
"ru-RU": "Pусский (Russian)",
"sr-SP": "српски (Serbian)",
@@ -55,6 +57,7 @@ export const LOCALES = [{% for locale in locales %}
progress: {{ locale.progress }},
},{% endfor %}
]
"""
@@ -66,6 +69,9 @@ class TargetLanguage(MealieModel):
twoLettersCode: str
progress: float = 0.0
class Config:
extra = Extra.allow
class CrowdinApi:
project_name = "Mealie"
@@ -122,22 +128,53 @@ class CrowdinApi:
return response.json()
def main():
print("Starting...") # noqa
PROJECT_DIR = Path(__file__).parent.parent.parent
if API_KEY is None:
print("CROWDIN_API_KEY is not set") # noqa
return
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 inject_nuxt_values():
all_date_locales = [
f'"{match.stem}": require("./lang/dateTimeFormats/{match.name}"),' for match in datetime_dir.glob("*.json")
]
all_langs = []
for match in locales_dir.glob("*.json"):
lang_string = f'{{ code: "{match.stem}", file: "{match.name}" }},'
all_langs.append(lang_string)
log.debug(f"injecting locales into nuxt config -> {nuxt_config}")
inject_inline(nuxt_config, CodeKeys.nuxt_local_messages, all_langs)
inject_inline(nuxt_config, CodeKeys.nuxt_local_dates, all_date_locales)
def generate_locales_ts_file():
api = CrowdinApi("")
models = api.get_languages()
tmpl = Template(LOCALE_TEMPLATE)
rendered = tmpl.render(locales=models)
with open(_static.CodeDest.use_locales, "w") as f:
log.debug(f"generating locales ts file -> {CodeDest.use_locales}")
with open(CodeDest.use_locales, "w") as f:
f.write(rendered) # type:ignore
print("Finished...") # noqa
def main():
if API_KEY is None or API_KEY == "":
log.error("CROWDIN_API_KEY is not set")
return
generate_locales_ts_file()
inject_nuxt_values()
if __name__ == "__main__":

View File

@@ -2,28 +2,28 @@ from pathlib import Path
from jinja2 import Template
from pydantic2ts import generate_typescript_defs
from rich import print
from utils import log
# ============================================================
# 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";
{% for name in global %}import {{ name }} from "@/components/global/{{ name }}.vue";
{% endfor %}{% for name in layout %}import {{ name }} from "@/components/layout/{{ 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 %}
}
{% for name in global -%}
{{ " " }}{{ name }}: typeof {{ name }};
{% endfor -%}
{{ " " }}// Layout Components
{% for name in layout -%}
{{ " " }}{{ name }}: typeof {{ name }};
{% endfor -%}{{ " }"}}
}
export {};
"""
CWD = Path(__file__).parent
@@ -46,6 +46,7 @@ def generate_global_components_types() -> None:
data = {}
for name, path in component_paths.items():
components = [component.stem for component in path.glob("*.vue")]
components.sort()
data[name] = components
return data
@@ -74,7 +75,7 @@ def generate_typescript_types() -> None:
return str_path
schema_path = PROJECT_DIR / "mealie" / "schema"
types_dir = PROJECT_DIR / "frontend" / "types" / "api-types"
types_dir = PROJECT_DIR / "frontend" / "lib" / "api" / "types"
ignore_dirs = ["__pycache__", "static", "_mealie"]
@@ -99,26 +100,29 @@ def generate_typescript_types() -> None:
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
log.error(f"Module Error: {e}")
print("\n📁 Skipped Directories:") # noqa
log.debug("\n📁 Skipped Directories:")
for skipped_dir in skipped_dirs:
print(" 📁", skipped_dir.name) # noqa
log.debug(f" 📁 {skipped_dir.name}")
print("📄 Skipped Files:") # noqa
log.debug("📄 Skipped Files:")
for f in skipped_files:
print(" 📄", f.name) # noqa
log.debug(f" 📄 {f.name}")
print("❌ Failed Modules:") # noqa
for f in failed_modules:
print("", f.name) # noqa
if len(failed_modules) > 0:
log.error("❌ Failed Modules:")
for f in failed_modules:
log.error(f"{f.name}")
def main():
log.debug("\n-- Starting Global Components Generator --")
generate_global_components_types()
log.debug("\n-- Starting Pydantic To Typescript Generator --")
generate_typescript_types()
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()
main()

View File

@@ -0,0 +1,28 @@
from pathlib import Path
import gen_py_pytest_data_paths
import gen_py_pytest_routes
import gen_py_schema_exports
import gen_ts_locales
import gen_ts_types
from utils import log
CWD = Path(__file__).parent
def main():
items = [
(gen_py_schema_exports.main, "schema exports"),
(gen_ts_types.main, "frontend types"),
(gen_ts_locales.main, "locales"),
(gen_py_pytest_data_paths.main, "test data paths"),
(gen_py_pytest_routes.main, "pytest routes"),
]
for func, name in items:
log.info(f"Generating {name}...")
func()
if __name__ == "__main__":
main()

View File

@@ -1,9 +1,11 @@
{% 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 %}
# This Content is Auto Generated for Pytest
prefix = "{{paths.prefix}}"
{% for path in paths.static_paths %}
{{ path.router_slug }} = "{{path.prefix}}{{ path.route }}"
"""`{{path.prefix}}{{ path.route }}`"""{% endfor %}
{% for path in paths.function_paths %}
def {{path.router_slug}}({{path.var|join(", ")}}):
"""`{{ paths.prefix }}{{ path.route }}`"""
return f"{prefix}{{ path.route }}"
{% endfor %}

View File

@@ -0,0 +1,25 @@
from .open_api_parser import OpenAPIParser
from .route import HTTPRequest, ParameterIn, RequestBody, RequestType, RouteObject, RouterParameter
from .static import PROJECT_DIR, CodeDest, CodeKeys, CodeTemplates, Directories
from .template import CodeSlicer, find_start_end, get_indentation_of_string, inject_inline, log, render_python_template
__all__ = [
"CodeDest",
"CodeKeys",
"CodeSlicer",
"CodeTemplates",
"Directories",
"find_start_end",
"get_indentation_of_string",
"HTTPRequest",
"inject_inline",
"log",
"OpenAPIParser",
"ParameterIn",
"PROJECT_DIR",
"render_python_template",
"RequestBody",
"RequestType",
"RouteObject",
"RouterParameter",
]

View File

@@ -3,11 +3,12 @@ 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
from .static import Directories
def get_openapi_spec_by_ref(app, type_reference: str) -> dict:
if not type_reference:

View File

@@ -3,7 +3,7 @@ from enum import Enum
from typing import Optional
from humps import camelize
from pydantic import BaseModel, Field
from pydantic import BaseModel, Extra, Field
from slugify import slugify
@@ -30,6 +30,7 @@ class RequestType(str, Enum):
class ParameterIn(str, Enum):
query = "query"
path = "path"
header = "header"
class RouterParameter(BaseModel):
@@ -37,10 +38,16 @@ class RouterParameter(BaseModel):
name: str
location: ParameterIn = Field(..., alias="in")
class Config:
extra = Extra.allow
class RequestBody(BaseModel):
required: bool = False
class Config:
extra = Extra.allow
class HTTPRequest(BaseModel):
request_type: RequestType
@@ -49,7 +56,10 @@ class HTTPRequest(BaseModel):
requestBody: Optional[RequestBody]
parameters: list[RouterParameter] = []
tags: list[str]
tags: list[str] | None = []
class Config:
extra = Extra.allow
def list_as_js_object_string(self, parameters, braces=True):
if len(parameters) == 0:

View File

@@ -0,0 +1,26 @@
from pathlib import Path
PARENT = Path(__file__).parent.parent
PROJECT_DIR = Path(__file__).parent.parent.parent.parent
class Directories:
out_dir = PARENT / "generated"
class CodeTemplates:
interface = PARENT / "templates" / "interface.js"
pytest_routes = PARENT / "templates" / "test_routes.py.j2"
class CodeDest:
interface = PARENT / "generated" / "interface.js"
pytest_routes = PARENT / "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

@@ -1,3 +1,4 @@
import logging
import re
from dataclasses import dataclass
from pathlib import Path
@@ -5,9 +6,15 @@ from pathlib import Path
import black
import isort
from jinja2 import Template
from rich.logging import RichHandler
FORMAT = "%(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT, datefmt="[%X]", handlers=[RichHandler()])
log = logging.getLogger("rich")
def render_python_template(template_file: Path | str, dest: Path, data: dict) -> str:
def render_python_template(template_file: Path | str, dest: Path, data: dict):
"""Render and Format a Jinja2 Template for Python Code"""
if isinstance(template_file, Path):
tplt = Template(template_file.read_text())
@@ -15,7 +22,9 @@ def render_python_template(template_file: Path | str, dest: Path, data: dict) ->
tplt = Template(template_file)
text = tplt.render(data=data)
text = black.format_str(text, mode=black.FileMode())
dest.write_text(text)
isort.file(dest)
@@ -37,7 +46,6 @@ class CodeSlicer:
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
@@ -46,7 +54,7 @@ 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]:
def find_start_end(file_text: list[str], gen_id: str) -> tuple[int, int, str]:
start = None
end = None
indentation = None
@@ -84,7 +92,7 @@ def inject_inline(file_path: Path, key: str, code: list[str]) -> None:
"""
with open(file_path, "r") as f:
with open(file_path) as f:
file_text = f.readlines()
start, end, indentation = find_start_end(file_text, key)

View File

@@ -2,8 +2,13 @@ import json
import random
import string
import time
from dataclasses import dataclass
import requests
from rich.console import Console
from rich.table import Table
console = Console()
def random_string(length: int) -> str:
@@ -14,6 +19,218 @@ def payload_factory() -> dict:
return {"name": random_string(15)}
def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dict:
return {
"id": id,
"userId": userId,
"groupId": groupId,
"name": name,
"slug": slug,
"image": "tNRG",
"recipeYield": "9 servings",
"totalTime": "33 Minutes",
"prepTime": "20 Minutes",
"cookTime": None,
"performTime": "13 Minutes",
"description": "These Levain Bakery-Style Peanut Butter Cookies are the ULTIMATE for serious PB lovers! Supremely thick and chewy with gooey centers and a soft texture, they're packed with peanut butter flavor and Reese's Pieces for the most amazing cookie ever!",
"recipeCategory": [],
"tags": [],
"tools": [],
"rating": None,
"orgURL": "https://thedomesticrebel.com/2021/04/28/levain-bakery-style-ultimate-peanut-butter-cookies/",
"recipeIngredient": [
{
"title": None,
"note": "1 cup unsalted butter, cut into cubes",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "ea3b6702-9532-4fbc-a40b-f99917831c26",
},
{
"title": None,
"note": "1 cup light brown sugar",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "c5bbfefb-1e23-4ffd-af88-c0363a0fae82",
},
{
"title": None,
"note": "1/2 cup granulated white sugar",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "034f481b-c426-4a17-b983-5aea9be4974b",
},
{
"title": None,
"note": "2 large eggs",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "37c1f796-3bdb-4856-859f-dbec90bc27e4",
},
{
"title": None,
"note": "2 tsp vanilla extract",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "85561ace-f249-401d-834c-e600a2f6280e",
},
{
"title": None,
"note": "1/2 cup creamy peanut butter",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "ac91bda0-e8a8-491a-976a-ae4e72418cfd",
},
{
"title": None,
"note": "1 tsp cornstarch",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "4d1256b3-115e-4475-83cd-464fbc304cb0",
},
{
"title": None,
"note": "1 tsp baking soda",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "64627441-39f9-4ee3-8494-bafe36451d12",
},
{
"title": None,
"note": "1/2 tsp salt",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "7ae212d0-3cd1-44b0-899e-ec5bd91fd384",
},
{
"title": None,
"note": "1 cup cake flour",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "06967994-8548-4952-a8cc-16e8db228ebd",
},
{
"title": None,
"note": "2 cups all-purpose flour",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "bdb33b23-c767-4465-acf8-3b8e79eb5691",
},
{
"title": None,
"note": "2 cups peanut butter chips",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "12ba0af8-affd-4fb2-9cca-6f1b3e8d3aef",
},
{
"title": None,
"note": "1½ cups Reese's Pieces candies",
"unit": None,
"food": None,
"disableAmount": True,
"quantity": 1,
"originalText": None,
"referenceId": "4bdc0598-a3eb-41ee-8af0-4da9348fbfe2",
},
],
"dateAdded": "2022-09-03",
"dateUpdated": "2022-09-10T15:18:19.866085",
"createdAt": "2022-09-03T18:31:17.488118",
"updateAt": "2022-09-10T15:18:19.869630",
"recipeInstructions": [
{
"id": "60ae53a3-b3ff-40ee-bae3-89fea0b1c637",
"title": "",
"text": "Preheat oven to 410° degrees F. Line 2 baking sheets with parchment paper or silicone liners; set aside.",
"ingredientReferences": [],
},
{
"id": "4e1c30c2-2e96-4a0a-b750-23c9ea3640f8",
"title": "",
"text": "In the bowl of a stand mixer, cream together the cubed butter, brown sugar and granulated sugar with the paddle attachment for 30 seconds on low speed. Increase speed to medium and beat for another 30 seconds, then increase to medium-high speed and beat for another 30 seconds until mixture is creamy and smooth. Beat in the eggs, one at a time, followed by the vanilla extract and peanut butter, scraping down the sides and bottom of the bowl as needed.",
"ingredientReferences": [],
},
{
"id": "9fb8e2a2-d410-445c-bafc-c059203e6f4b",
"title": "",
"text": "Add in the cornstarch, baking soda, salt, cake flour, and all-purpose flour and mix on low speed until just combined. Fold in the peanut butter chips and Reese's Pieces candies by hand until fully incorporated. Chill the dough uncovered in the fridge for 15 minutes.",
"ingredientReferences": [],
},
{
"id": "1ceb9aa4-49f7-4d4a-996f-3c715eb74642",
"title": "",
"text": 'Using a digital kitchen scale for accuracy, weigh out 6 ounces of cookie dough in a loose, rough textured ball. I like to make my cookie dough balls kind of tall as well. You do not want the dough balls to be smooth and compacted. Place on the baking sheet. Repeat with remaining dough balls, staggering on the baking sheet at least 3" apart from one another, and only placing 4 dough balls per baking sheet.',
"ingredientReferences": [],
},
{
"id": "591993fc-72bb-4091-8a12-84640c523fc1",
"title": "",
"text": "Bake one baking sheet at a time in the center rack of the oven for 10-13 minutes or until the tops are light golden brown and the exterior is dry and dull looking. Centers will be slightly underdone and gooey; this is okay and the cookies will finish cooking some once removed from the oven. Let stand on the baking sheets for at least 30 minutes before serving; the cookies are very delicate and fragile once removed from the oven and need time to set before being moved. Keep remaining dough refrigerated while other cookies bake.",
"ingredientReferences": [],
},
],
"nutrition": {
"calories": None,
"fatContent": None,
"proteinContent": None,
"carbohydrateContent": None,
"fiberContent": None,
"sodiumContent": None,
"sugarContent": None,
},
"settings": {
"public": True,
"showNutrition": False,
"showAssets": False,
"landscapeView": False,
"disableComments": False,
"disableAmount": True,
"locked": False,
},
"assets": [],
"notes": [],
"extras": {},
"comments": [],
}
def login(username="changeme@email.com", password="MyPassword"):
payload = {"username": username, "password": password}
@@ -30,32 +247,90 @@ def populate_data(token):
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)
console.print(f"Error: {r.status_code}")
console.print(r.text)
exit()
else:
print(f"Created recipe: {payload}")
recipe_json = requests.get(f"http://localhost:9000/api/recipes/{payload['name']}", headers=token)
if recipe_json.status_code != 200:
console.print(f"Error: {recipe_json.status_code}")
console.print(recipe_json.text)
exit()
recipe = json.loads(recipe_json.text)
update_data = recipe_data(recipe["name"], recipe["slug"], recipe["id"], recipe["userId"], recipe["groupId"])
r = requests.put(f"http://localhost:9000/api/recipes/{update_data['slug']}", json=update_data, headers=token)
if r.status_code != 200:
console.print(f"Error: {r.status_code}")
console.print(r.text)
exit()
def time_request(url, headers):
@dataclass(slots=True)
class Result:
recipes: int
time: float
def time_request(url, headers) -> Result:
start = time.time()
r = requests.get(url, headers=headers)
print(f"Total Recipes {len(r.json())}")
end = time.time()
print(end - start)
return Result(len(r.json()["items"]), end - start)
def main():
print("Starting...")
token = login()
# populate_data(token)
for _ in range(10):
time_request("http://localhost:9000/api/recipes", token)
results: list[Result] = []
print("Finished...")
for _ in range(10):
result = time_request("http://localhost:9000/api/recipes?perPage=-1&page=1&loadFood=true", token)
results.append(result)
min, max, average = 99, 0, 0
for result in results:
if result.time < min:
min = result.time
if result.time > max:
max = result.time
average += result.time
tbl1 = Table(title="Requests")
tbl1.add_column("Recipes", justify="right", style="cyan", no_wrap=True)
tbl1.add_column("Time", justify="right", style="magenta")
for result in results:
tbl1.add_row(
str(result.recipes),
str(result.time),
)
tbl2 = Table(title="Summary")
tbl2.add_column("Min", justify="right", style="green")
tbl2.add_column("Max", justify="right", style="green")
tbl2.add_column("Average", justify="right", style="green")
tbl2.add_row(
str(round(min * 1000)) + "ms",
str(round(max * 1000)) + "ms",
str(round((average / len(results)) * 1000)) + "ms",
)
console = Console()
console.print(tbl1)
console.print(tbl2)
# Best Time 289 / 405/ 247
if __name__ == "__main__":

View File

@@ -1,214 +0,0 @@
import json
import re
from enum import Enum
from itertools import groupby
from pathlib import Path
from typing import Optional
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
CWD = Path(__file__).parent
OUT_DIR = CWD / "output"
TEMPLATES_DIR = CWD / "templates"
JS_DIR = OUT_DIR / "javascriptAPI"
JS_DIR.mkdir(exist_ok=True, parents=True)
class RouteObject:
def __init__(self, route_string) -> None:
self.prefix = "/" + route_string.split("/")[1]
self.route = "/" + route_string.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 * ")
class PathObject(BaseModel):
route_object: RouteObject
http_verbs: list[HTTPRequest]
class Config:
arbitrary_types_allowed = True
def get_path_objects(app: FastAPI):
paths = []
for key, value in app.openapi().items():
if key == "paths":
for key, value in value.items():
paths.append(
PathObject(
route_object=RouteObject(key),
http_verbs=[HTTPRequest(request_type=k, **v) for k, v in value.items()],
)
)
return paths
def 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_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(PYTHON_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 tag, tag_paths in groupby(paths, lambda x: x.http_verbs[0].tags[0]):
file_name = slugify(tag, separator="-")
tag = camelize(tag)
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))
content = template.render(files={"files": all_tags})
with open(JS_DIR.joinpath("index.js"), "w") as f:
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

@@ -1,26 +0,0 @@
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

@@ -1,159 +0,0 @@
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

@@ -1,32 +0,0 @@
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

@@ -1,156 +0,0 @@
# This Content is Auto Generated for Pytest
class AppRoutes:
def __init__(self) -> None:
self.prefix = '/api'
self.about_events = "/api/about/events"
self.about_events_notifications = "/api/about/events/notifications"
self.about_events_notifications_test = "/api/about/events/notifications/test"
self.about_recipes_defaults = "/api/about/recipes/defaults"
self.auth_refresh = "/api/auth/refresh"
self.auth_token = "/api/auth/token"
self.auth_token_long = "/api/auth/token/long"
self.backups_available = "/api/backups/available"
self.backups_export_database = "/api/backups/export/database"
self.backups_upload = "/api/backups/upload"
self.categories = "/api/categories"
self.categories_empty = "/api/categories/empty"
self.debug = "/api/debug"
self.debug_last_recipe_json = "/api/debug/last-recipe-json"
self.debug_log = "/api/debug/log"
self.debug_statistics = "/api/debug/statistics"
self.debug_version = "/api/debug/version"
self.groups = "/api/groups"
self.groups_self = "/api/groups/self"
self.meal_plans_all = "/api/meal-plans/all"
self.meal_plans_create = "/api/meal-plans/create"
self.meal_plans_this_week = "/api/meal-plans/this-week"
self.meal_plans_today = "/api/meal-plans/today"
self.meal_plans_today_image = "/api/meal-plans/today/image"
self.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_uncategorized = "/api/recipes/summary/uncategorized"
self.recipes_summary_untagged = "/api/recipes/summary/untagged"
self.recipes_tag = "/api/recipes/tag"
self.recipes_test_scrape_url = "/api/recipes/test-scrape-url"
self.shopping_lists = "/api/shopping-lists"
self.site_settings = "/api/site-settings"
self.site_settings_custom_pages = "/api/site-settings/custom-pages"
self.site_settings_webhooks_test = "/api/site-settings/webhooks/test"
self.tags = "/api/tags"
self.tags_empty = "/api/tags/empty"
self.themes = "/api/themes"
self.themes_create = "/api/themes/create"
self.users = "/api/users"
self.users_api_tokens = "/api/users/api-tokens"
self.users_self = "/api/users/self"
self.users_sign_ups = "/api/users/sign-ups"
self.utils_download = "/api/utils/download"
def about_events_id(self, id):
return f"{self.prefix}/about/events/{id}"
def about_events_notifications_id(self, id):
return f"{self.prefix}/about/events/notifications/{id}"
def backups_file_name_delete(self, file_name):
return f"{self.prefix}/backups/{file_name}/delete"
def backups_file_name_download(self, file_name):
return f"{self.prefix}/backups/{file_name}/download"
def backups_file_name_import(self, file_name):
return f"{self.prefix}/backups/{file_name}/import"
def categories_category(self, category):
return f"{self.prefix}/categories/{category}"
def debug_log_num(self, num):
return f"{self.prefix}/debug/log/{num}"
def groups_id(self, id):
return f"{self.prefix}/groups/{id}"
def meal_plans_id(self, id):
return f"{self.prefix}/meal-plans/{id}"
def meal_plans_id_shopping_list(self, id):
return f"{self.prefix}/meal-plans/{id}/shopping-list"
def meal_plans_plan_id(self, plan_id):
return f"{self.prefix}/meal-plans/{plan_id}"
def media_recipes_recipe_slug_assets_file_name(self, recipe_slug, file_name):
return f"{self.prefix}/media/recipes/{recipe_slug}/assets/{file_name}"
def media_recipes_recipe_slug_images_file_name(self, recipe_slug, file_name):
return f"{self.prefix}/media/recipes/{recipe_slug}/images/{file_name}"
def migrations_import_type_file_name_delete(self, import_type, file_name):
return f"{self.prefix}/migrations/{import_type}/{file_name}/delete"
def migrations_import_type_file_name_import(self, import_type, file_name):
return f"{self.prefix}/migrations/{import_type}/{file_name}/import"
def migrations_import_type_upload(self, import_type):
return f"{self.prefix}/migrations/{import_type}/upload"
def recipes_recipe_slug(self, recipe_slug):
return f"{self.prefix}/recipes/{recipe_slug}"
def recipes_recipe_slug_assets(self, recipe_slug):
return f"{self.prefix}/recipes/{recipe_slug}/assets"
def recipes_recipe_slug_image(self, recipe_slug):
return f"{self.prefix}/recipes/{recipe_slug}/image"
def recipes_recipe_slug_zip(self, recipe_slug):
return f"{self.prefix}/recipes/{recipe_slug}/zip"
def recipes_slug_comments(self, slug):
return f"{self.prefix}/recipes/{slug}/comments"
def recipes_slug_comments_id(self, slug, id):
return f"{self.prefix}/recipes/{slug}/comments/{id}"
def shopping_lists_id(self, id):
return f"{self.prefix}/shopping-lists/{id}"
def site_settings_custom_pages_id(self, id):
return f"{self.prefix}/site-settings/custom-pages/{id}"
def tags_tag(self, tag):
return f"{self.prefix}/tags/{tag}"
def themes_id(self, id):
return f"{self.prefix}/themes/{id}"
def users_api_tokens_token_id(self, token_id):
return f"{self.prefix}/users/api-tokens/{token_id}"
def users_id(self, id):
return f"{self.prefix}/users/{id}"
def users_id_favorites(self, id):
return f"{self.prefix}/users/{id}/favorites"
def users_id_favorites_slug(self, id, slug):
return f"{self.prefix}/users/{id}/favorites/{slug}"
def users_id_image(self, id):
return f"{self.prefix}/users/{id}/image"
def users_id_password(self, id):
return f"{self.prefix}/users/{id}/password"
def users_id_reset_password(self, id):
return f"{self.prefix}/users/{id}/reset-password"
def users_sign_ups_token(self, token):
return f"{self.prefix}/users/sign-ups/{token}"

View File

@@ -1,11 +0,0 @@
git checkout dev
git merge --strategy=ours master # keep the content of this branch, but record a merge
git checkout master
git merge dev # fast-forward master up to the merge
## TODOs
# Create New Branch v0.x.x
# Push Branch Version to Github
# Create Pull Request

View File

@@ -1,17 +0,0 @@
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 +0,0 @@
{% for api in files.files %}
import { {{ api.camel }}API } from "./interfaces/{{ api.slug }}" {% endfor %}
export const api = {
{% for api in files.files %}
{{api.camel}}: {{api.camel}}API, {% endfor %}
}

View File

@@ -1,12 +0,0 @@
# This Content is Auto Generated for Pytest
class AppRoutes:
def __init__(self) -> None:
self.prefix = '{{paths.prefix}}'
{% for path in paths.static_paths %}
self.{{ path.router_slug }} = "{{path.prefix}}{{ path.route }}"{% endfor %}
{% for path in paths.function_paths %}
def {{path.router_slug}}(self, {{path.var|join(", ")}}):
return f"{self.prefix}{{ path.route }}"
{% endfor %}

View File

@@ -3,6 +3,10 @@ services:
mealie-frontend:
container_name: mealie-frontend
image: mealie-frontend:dev
deploy:
resources:
limits:
memory: 500M
build:
context: ./frontend
dockerfile: Dockerfile
@@ -34,6 +38,10 @@ services:
- THEME_DARK_ERROR=#EF5350
mealie:
container_name: mealie-api
deploy:
resources:
limits:
memory: 1000M
build:
context: ./
target: production
@@ -57,6 +65,7 @@ services:
# =====================================
# Web Concurrency
WEB_GUNICORN: true
WORKERS_PER_CORE: 0.5
MAX_WORKERS: 1
WEB_CONCURRENCY: 1

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

View File

@@ -67,6 +67,6 @@ This is, what I think, is a big release! Tons of new features and some great qua
### Breaking Changes
!!! error "Breaking Changes"
- API endpoints have been refactored to adhear to a more consistent standard. This is a WIP and more changes are likely to occur.
- API endpoints have been refactored to adhere to a more consistent standard. This is a WIP and more changes are likely to occur.
- Officially Dropped MongoDB Support
- Database Breaks! We have not yet implemented a database migration service. As such, upgrades cannot be done by simply pulling the image. You must first export your recipes, update your deployment, and then import your recipes. This pattern is likely to be how upgrades take place prior to v1.0. After v1.0 migrations will be done automatically.

View File

@@ -73,7 +73,7 @@
- See what's for dinner
- Manage Long Live API Tokens (New)
- New Admin Dashboard! 🎉
- Now you can get some insight on your application with application statics and events.
- Now you can get some insight on your application with application statistics and events.
- See uncategorized/untagged recipes and organize them!
- Backup/Restore right from your dashboard
- See server side events. Now you can know who deleted your favorite recipe!

View File

@@ -73,7 +73,7 @@
- Site Settings has been completely revamped. All site-wide settings at defined on the server as ENV variables. The site settings page now only shows you the non-secret values for reference. It also has some helpers to let you know if something isn't configured correctly.
- Server Side Bare URL will let you know if the BASE_URL env variable has been set
- Secure Site let's you know if you're serving via HTTPS or accessing by localhost. accessing without a secure site will render some of the features unusable.
- Secure Site let's you know if you're serving via HTTPS or accessing by localhost. Accessing without a secure site will render some of the features unusable.
- Email Configuration Status will let you know if all the email settings have been provided and offer a way to send test emails.
#### 👨‍👩‍👧‍👦 Users and Groups

View File

@@ -0,0 +1,126 @@
### Security
#### v1.0.0beta-3 and Under - Recipe Scraper: Server Side Request Forgery Lead To Denial Of Service
!!! error "CWE-918: Server-Side Request Forgery (SSRF)"
In this case if a attacker try to load a huge file then server will try to load the file and eventually server use its all memory which will dos the server
##### Mitigation
HTML is now scraped via a Stream and canceled after a 15 second timeout to prevent arbitrary data from being loaded into the server.
#### v1.0.0beta-3 and Under - Recipe Assets: Remote Code Execution
!!! error "CWE-1336: Improper Neutralization of Special Elements Used in a Template Engine"
As a low privileged user, Create a new recipe and click on the "+" to add a New Asset.
Select a file, then proxy the request that will create the asset.
Since mealie/routes/recipe/recipe_crud_routes.py:306 is calling slugify on the name POST parameter, we use $ which slugify() will remove completely.
Since mealie/routes/recipe/recipe_crud_routes.py:306 is concatenating raw user input from the extension POST parameter into the variable file_name, which ultimately gets used when writing to disk, we can use a directory traversal attack in the extension (e.g. ./../../../tmp/pwn.txt) to write the file to arbitrary location on the server.
As an attacker, now that we have a strong attack primitive, we can start getting creative to get RCE. Since the files were being created by root, we could add an entry to /etc/passwd, create a crontab, etc. but since there was templating functionality in the application that peaked my interest. The PoC in the HTTP request above creates a Jinja2 template at /app/data/template/pwn.html. Since Jinja2 templates execute Python code when rendered, all we have to do now to get code execution is render the malicious template. This was easy enough.
##### Mitigation
We've added proper path sanitization to ensure that the user is not allowed to write to arbitrary locations on the server.
!!! warning "Breaking Change Incoming"
As this has shown a significant area of exposure in the templates that Mealie was provided for exporting recipes, we'll be removing this feature in the next Beta release and will instead rely on the community to provide tooling around transforming recipes using templates. This will significantly limit the possible exposure of users injecting malicious templates into the application. The template functionality will be completely removed in the next beta release v1.0.0beta-5
#### All version Markdown Editor: Cross Site Scripting
!!! error "CWE-79: Cross-site Scripting (XSS) - Stored"
A low privilege user can insert malicious JavaScript code into the Recipe Instructions which will execute in another person's browser that visits the recipe.
`<img src=x onerror=alert(document.domain)>`
##### Mitigation
This issues is present on all pages that allow markdown input. This error has been mitigated by wrapping the 3rd Party Markdown component and using the `domPurify` library to strip out the dangerous HTML.
#### v1.0.0beta-3 and Under - Image Scraper: Server-Side Request Forgery
!!! error "CWE-918: Server-Side Request Forgery (SSRF)"
In the recipe edit page, is possible to upload an image directly or via an URL provided by the user. The function that handles the fetching and saving of the image via the URL doesn't have any URL verification, which allows to fetch internal services.
Furthermore, after the resource is fetch, there is no MIME type validation, which would ensure that the resource is indeed an image. After this, because there is no extension in the provided URL, the application will fallback to jpg, and original for the image name.
Then the result is saved to disk with the original.jpg name, that can be retrieved from the following URL: http://<domain>/api/media/recipes/<recipe-uid>/images/original.jpg. This file will contain the full response of the provided URL.
**Impact**
An attacker can get sensitive information of any internal-only services running. For example, if the application is hosted on Amazon Web Services (AWS) platform, its possible to fetch the AWS API endpoint, https://169.254.169.254, which returns API keys and other sensitive metadata.
##### Mitigation
Two actions were taken to reduce exposure to SSRF in this case.
1. The application will not prevent requests being made to local resources by checking for localhost or 127.0.0.1 domain names.
2. The mime-type of the response is now checked prior to writing to disk.
If either of the above actions prevent the user from uploading images, the application will alert the user of what error occurred.
### Bug Fixes
- For erroneously-translated datetime config ([#1362](https://github.com/hay-kot/mealie/issues/1362))
- Fixed text color on RecipeCard in RecipePrintView and implemented ingredient sections ([#1351](https://github.com/hay-kot/mealie/issues/1351))
- Ingredient sections lost after parsing ([#1368](https://github.com/hay-kot/mealie/issues/1368))
- Increased float rounding precision for CRF parser ([#1369](https://github.com/hay-kot/mealie/issues/1369))
- Infinite scroll bug on all recipes page ([#1393](https://github.com/hay-kot/mealie/issues/1393))
- Fast fail of bulk importer ([#1394](https://github.com/hay-kot/mealie/issues/1394))
- Bump @mdi/js from 5.9.55 to 6.7.96 in /frontend ([#1279](https://github.com/hay-kot/mealie/issues/1279))
- Bump @nuxtjs/i18n from 7.0.3 to 7.2.2 in /frontend ([#1288](https://github.com/hay-kot/mealie/issues/1288))
- Bump date-fns from 2.23.0 to 2.28.0 in /frontend ([#1293](https://github.com/hay-kot/mealie/issues/1293))
- Bump fuse.js from 6.5.3 to 6.6.2 in /frontend ([#1325](https://github.com/hay-kot/mealie/issues/1325))
- Bump core-js from 3.17.2 to 3.23.1 in /frontend ([#1383](https://github.com/hay-kot/mealie/issues/1383))
- All-recipes page now sorts alphabetically ([#1405](https://github.com/hay-kot/mealie/issues/1405))
- Sort recent recipes by created_at instead of date_added ([#1417](https://github.com/hay-kot/mealie/issues/1417))
- Only show scaler when ingredients amounts enabled ([#1426](https://github.com/hay-kot/mealie/issues/1426))
- Add missing types for API token deletion ([#1428](https://github.com/hay-kot/mealie/issues/1428))
- Entry nutrition checker ([#1448](https://github.com/hay-kot/mealie/issues/1448))
- Use == operator instead of is_ for sql queries ([#1453](https://github.com/hay-kot/mealie/issues/1453))
- Use `mtime` instead of `ctime` for backup dates ([#1461](https://github.com/hay-kot/mealie/issues/1461))
- Mealplan pagination ([#1464](https://github.com/hay-kot/mealie/issues/1464))
- Properly use pagination for group event notifies ([#1512](https://github.com/hay-kot/mealie/pull/1512))
### Documentation
- Add go bulk import example ([#1388](https://github.com/hay-kot/mealie/issues/1388))
- Fix old link
- Pagination and filtering, and fixed a few broken links ([#1488](https://github.com/hay-kot/mealie/issues/1488))
### Features
- Toggle display of ingredient references in recipe instructions ([#1268](https://github.com/hay-kot/mealie/issues/1268))
- Add custom scaling option ([#1345](https://github.com/hay-kot/mealie/issues/1345))
- Implemented "order by" API parameters for recipe, food, and unit queries ([#1356](https://github.com/hay-kot/mealie/issues/1356))
- Implement user favorites page ([#1376](https://github.com/hay-kot/mealie/issues/1376))
- Extend Apprise JSON notification functionality with programmatic data ([#1355](https://github.com/hay-kot/mealie/issues/1355))
- Mealplan-webhooks ([#1403](https://github.com/hay-kot/mealie/issues/1403))
- Added "last-modified" header to supported record types ([#1379](https://github.com/hay-kot/mealie/issues/1379))
- Re-write get all routes to use pagination ([#1424](https://github.com/hay-kot/mealie/issues/1424))
- Advanced filtering API ([#1468](https://github.com/hay-kot/mealie/issues/1468))
- Restore frontend sorting for all recipes ([#1497](https://github.com/hay-kot/mealie/issues/1497))
- Implemented local storage for sorting and dynamic sort icons on the new recipe sort card ([1506](https://github.com/hay-kot/mealie/pull/1506))
- create new foods and units from their Data Management pages ([#1511](https://github.com/hay-kot/mealie/pull/1511))
### Miscellaneous Tasks
- Bump dev deps ([#1418](https://github.com/hay-kot/mealie/issues/1418))
- Bump @vue/runtime-dom in /frontend ([#1423](https://github.com/hay-kot/mealie/issues/1423))
- Backend page_all route cleanup ([#1483](https://github.com/hay-kot/mealie/issues/1483))
### Refactor
- Remove depreciated repo call ([#1370](https://github.com/hay-kot/mealie/issues/1370))
### Hotfix
- Tame typescript beast
### UI
- Improve parser ui text display ([#1437](https://github.com/hay-kot/mealie/issues/1437))
<!-- generated by git-cliff -->

View File

@@ -0,0 +1,3 @@
## NOTICE:
Release changelogs are now published on github releases. This file is kept for historical purposes.

View File

@@ -36,7 +36,7 @@ Checkout the makefile for all of the available commands.
Once the prerequisites are installed you can cd into the project base directory and run `make setup` to install the python and node dependencies.
=== "Linux / MacOs"
=== "Linux / macOS"
```bash
# Naviate To The Root Directory
@@ -66,7 +66,7 @@ Before you start the server you MUST copy the `template.env` and `frontend/templ
Once that is complete you're ready to start the servers. You'll need two shells open, One for the server and one for the frontend.
=== "Linux / MacOs"
=== "Linux / macOS"
```bash
# Terminal #1

View File

@@ -4,7 +4,7 @@ Mealie uses Conditional Random Fields (CRFs) for parsing and processing ingredie
## Improving The CRF Parser
To improve results with the model, you'll likely need to focus on improving the tokenization and parsing of the original string to aid the model in determine what the ingredient is. Datascience is not my forte, but I have done some tokenization to improve the model. You can find that code under `/mealie/services/parser_services/crfpp` along with some other utility functions to aid in the tokenization and processing of ingredient strings.
To improve results with the model, you'll likely need to focus on improving the tokenization and parsing of the original string to aid the model in determine what the ingredient is. Data science is not my forte, but I have done some tokenization to improve the model. You can find that code under `/mealie/services/parser_services/crfpp` along with some other utility functions to aid in the tokenization and processing of ingredient strings.
The best way to test on improving the parser is to register additional test cases in `/mealie/tests/unit_tests/test_crfpp_parser.py` and run the test after making changes to the tokenizer. Note that the test cases DO NOT run in the CI environment, therefore you will need to have CRF++ installed on your machine. If you're using a Mac the easiest way to do this is through brew.

View File

@@ -8,7 +8,7 @@ Recipes can be imported in bulk from a file containing a list of URLs. This can
```bash
#!/bin/bash
function authentification () {
function authentication () {
auth=$(curl -X 'POST' \
"$3/api/auth/token" \
-H 'accept: application/json' \
@@ -38,17 +38,20 @@ password="MyPassword"
mealie_url=http://localhost:9000
token=$(authentification $mail $password $mealie_url)
token=$(authentication $mail $password $mealie_url)
import_from_file $input $token $mealie_url
```
#### Go
See <a href="https://github.com/Jleagle/mealie-importer" target="_blank">Jleagle/mealie-importer</a>.
#### Python
```python
import requests
import re
def authentification(mail, password, mealie_url):
def authentication(mail, password, mealie_url):
headers = {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
@@ -87,7 +90,7 @@ password="MyPassword"
mealie_url="http://localhost:9000"
token = authentification(mail, password, mealie_url)
token = authentication(mail, password, mealie_url)
import_from_file(input_file, token, mealie_url)
```

View File

@@ -8,10 +8,85 @@ Mealie supports long-live api tokens in the user frontend. See [user settings pa
## Key Components
### Exploring Your Local API
On your local installation you can access interactive API documentation that provides `curl` examples and expected results. This allows you to easily test and interact with your API to identify places to include your own functionality. You can visit the documentation at `http://mealie.yourdomain.com/docs` or see the example at the [Demo Site](https://mealie-demo.hay-kot.dev/docs)
On your local installation you can access interactive API documentation that provides `curl` examples and expected results. This allows you to easily test and interact with your API to identify places to include your own functionality. You can visit the documentation at `http://mealie.yourdomain.com/docs` or see the example at the [Demo Site](https://demo.mealie.io/docs).
### Recipe Extras
Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.
### Extras
#### Recipe Extras
Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.
For example you could add `{"message": "Remember to thaw the chicken"}` to a recipe and use the webhooks built into mealie to send that message payload to a destination to be processed.
#### Shopping List and Food Extras
Similarly to recipes, extras are supported on shopping lists, shopping list items, and foods. At this time they are only accessible through the API. Extras for these objects allow for rich integrations between the Mealie shopping list and your favorite list manager, such as Alexa, ToDoist, Trello, or any other list manager with an API.
To keep shopping lists in sync, for instance, you can store your Trello list id on your Mealie shopping list: <br />
`{"trello_list_id": "5abbe4b7ddc1b351ef961414"}`
Now if an update is made to your shopping list, you know which Trello list also needs to be updated. Similarly, you can also keep track of individual cards on your Trello list by storing data on shopping list items: <br />
`{"trello_card_id": "bab414bde415cd715efb9361"}`
Sometimes you may want to exclude certain foods from syncing to your external list, such as water, so you can add a custom property to your "water" food: <br />
`{"trello_exclude_food": "true"}`
You can combine your custom data definitions with our Event Subscriptions API, enabling you to keep your external list up-to-date in real time.
### Pagination and Filtering
Most document types share a uniform pagination and filtering API (e.g. `GET /api/recipes`). These allow you to filter by an arbitrary combination of criteria and return only a certain number of documents (i.e. a single "page" of documents).
#### Pagination
The pagination API allows you to limit how many documents you return in each call. This is important when serving data to an application, as you don't want to wait for a huge payload every time you load a page. You may also not want to render all documents at once, opting to render only a few at a time.
The `perPage` parameter tells Mealie how many documents to return (this is similar to `LIMIT` in SQL). If you want to keep fetching more data in batches, first determine your batch size (in other words: how many documents you want per-page), then make additional calls by changing the `page` parameter. If your `perPage` size is 30, then page 1 will return the first 30 documents, page 2 will return the next 30 documents, etc.
Many applications will keep track of the query and adjust the page parameter appropriately, but some applications can't do this, or a particular implementation may make this difficult. The response includes pagination guides to help you find the next page and previous page. Here is a sample response:
```json
{
"page": 2,
"per_page": 5,
"total": 23,
"total_pages": 5,
"data": [...],
"next": "/recipes?page=3&per_page=5&order_by=name&order_direction=asc",
"previous": "/recipes?page=1&per_page=5&order_by=name&order_direction=asc"
}
```
Notice that the route does not contain the baseurl (e.g. `https://mymealieapplication.com/api`).
There are a few shorthands available to reduce the number of calls for certain common requests:
- if you want to return _all_ results, effectively disabling pagination, set `perPage = -1` (and fetch the first page)
- if you want to fetch the _last_ page, set `page = -1`
#### Filtering
The `queryFilter` parameter enables fine-grained control over your query. You can filter by any combination of attributes connected by logical operators (`AND`, `OR`). You can also group attributes together using parenthesis. For string, date, or datetime literals, you should surround them in double quotes (e.g. `"Pasta Fagioli"`). If there are no spaces in your literal (such as dates) the API will probably parse it correctly, but it's recommended that you use quotes anyway.
Here are several examples of filters. These filter strings are not surrounded in quotes for ease of reading, but they are _strings_, so they will probably be in quotes in your language.
##### Simple Filters
Here is an example of a filter to find a recipe with the name "Pasta Fagioli": <br>
`name = "Pasta Fagioli"`
This filter will find all recipes created on or after a particular date: <br>
`createdAt >= "2021-02-22"`
> **_NOTE:_** The API uses Python's [dateutil parser](https://dateutil.readthedocs.io/en/stable/parser.html), which parses many different date/datetime formats.
This filter will find all units that have `useAbbreviation` disabled: <br>
`useAbbreviation = false`
##### Compound Filters
You can combine multiple filter statements using logical operators (`AND`, `OR`).
This filter will only return recipes named "Pasta Fagioli" or "Grandma's Brisket": <br>
`name = "Pasta Fagioli" OR name = "Grandma's Brisket"`
This filter will return all recipes created before a particular date, except for the one named "Ultimate Vegan Ramen Recipe With Miso Broth": <br>
`createdAt < "January 2nd, 2014" AND name <> "Ultimate Vegan Ramen Recipe With Miso Broth"`
This filter will return three particular recipes: <br>
`name = "Pasta Fagioli" OR name = "Grandma's Brisket" OR name = "Ultimate Vegan Ramen Recipe With Miso Broth"`
##### Advanced Filters
You can have multiple filter groups combined by logical operators. You can define a filter group with parenthesis.
Here's a filter that will find all recipes updated between two particular times, but exclude the "Pasta Fagioli" recipe: <br>
`(updatedAt > "2022-07-17T15:47:00Z" AND updatedAt < "2022-07-17T15:50:00Z") AND name <> "Pasta Fagioli"`

View File

@@ -8,7 +8,7 @@
Mealie offers two main ways to create recipes. You can use the integrated recipe-scraper to create recipes from hundreds of websites, or you can create recipes manually using the recipe editor.
[Demo](https://beta.mealie.io/recipe/create/url){ .md-button .md-button--primary .align-right }
[Demo](https://demo.mealie.io/recipe/create/url){ .md-button .md-button--primary .align-right }
### Importing Recipes
@@ -22,7 +22,7 @@ Mealie supports importing recipes from a few other sources besides websites. Cur
You can access these options on your installation at the `/group/migrations` page on your installation. If you'd like to see another source added, feel free to request so on Github.
[Demo](https://beta.mealie.io/group/data/foods){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/group/data/foods){ .md-button .md-button--primary }
### Organizing Recipes
@@ -34,13 +34,13 @@ Mealie has a robust and flexible recipe organization system with a few different
Categories are the overarching organizer for recipes. You can assign as many categories as you'd like to a recipe, but we recommend that you try to limit the categories you assign to a recipe to one or two. This helps keep categories as focused as possible while still allowing you to find recipes that are related to each other. For example, you might assign a recipe to the category **Breakfast**, **Lunch**, **Dinner**, or **Side**.
[Demo](https://beta.mealie.io/recipes/categories){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/recipes/categories){ .md-button .md-button--primary }
#### Tags
Tags, are nearly identical to categories in function but play a secondary role in some cases. As such, we recommend that you use tags freely to help you organize your recipes by more specific topics. For example, if a recipe can be frozen or is a great left-over meal, you could assign the tags **frozen** and **left-over** and easily filter for those at a later time.
[Demo](https://beta.mealie.io/recipes/tags){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/recipes/tags){ .md-button .md-button--primary }
#### Tools
@@ -48,11 +48,11 @@ Tools, are another way that some users like to organize their recipes. If a reci
Each of the above organizers can be filtered in searches, and have their own pages where you can view all the recipes that are associated with those organizers.
[Demo](https://beta.mealie.io/recipes/tools){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/recipes/tools){ .md-button .md-button--primary }
#### Cookbooks
Mealie also has the concept of cookbooks. These can be created inside of a group and can use a cross section of Categories, Tags, and Tools to filter recipes and view them in one specific page. Cookbooks are a great way to keep a supset of recipes easily accessible to you. You can think of them as a saved search results page. While most examples are simple, you can use as many organizers to filter a cookbook as you'd like.
Mealie also has the concept of cookbooks. These can be created inside of a group and can use a cross section of Categories, Tags, and Tools to filter recipes and view them in one specific page. Cookbooks are a great way to keep a subset of recipes easily accessible to you. You can think of them as a saved search results page. While most examples are simple, you can use as many organizers to filter a cookbook as you'd like.
#### Examples:
@@ -60,7 +60,7 @@ Mealie also has the concept of cookbooks. These can be created inside of a group
- Pasta Sides: Recipes that have both the **Side** category and the **Pasta** tag
- Dessert Breads: Recipes that have both the **Bread** category and the **Dessert** tag
[Demo](https://beta.mealie.io/group/cookbooks){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/group/cookbooks){ .md-button .md-button--primary }
## Meal Planning
@@ -69,13 +69,13 @@ Mealie uses a calendar like view to help you plan your meals. It shows you the p
!!! tip
You can also add a "Note" type entry to your meal-plan when you want to include something that might not have a specific recipes. This is great for leftovers, or for ordering out.
[Demo](https://beta.mealie.io/group/mealplan/planner){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/group/mealplan/planner){ .md-button .md-button--primary }
### Planner Rules
The meal planner has the concept of plan rules. These offer a flexible way to use your organizers to customize how a random recipe is inserted into your meal plan. You can set rules to restrict the pool of recipes based on the Tags and/or Categories of a recipe. Additionally, since meal plans have a Breakfast, Lunch, Dinner, and Snack labels you can specifically set a rule to be active for a **specific meal type** or even a **specific day of the week.**
[Demo](https://beta.mealie.io/group/mealplan/settings){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/group/mealplan/settings){ .md-button .md-button--primary }
## Shopping Lists
@@ -85,7 +85,7 @@ The shopping lists feature is a great way to keep track of what you need to buy
At this time there isn't a tight integration between meal-plans and shopping lists, however it's something we have planned for the future.
[Demo](https://beta.mealie.io/shopping-lists){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/shopping-lists){ .md-button .md-button--primary }
## Data Management
@@ -107,31 +107,31 @@ Managing a robust collection of recipes inevitable requires a lot of data. Meali
- Merge Units into a single unit entry
- Export as JSON
[Demo](https://beta.mealie.io/group/data/foods){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/group/data/foods){ .md-button .md-button--primary }
## Server Administration
### Site Settings
The site settings page contains general information about your installtion like the application version, some configuration details, and some utilities to help you confirm your installation is working as expected. For example, you can use the Email Configuration section to validate that your email credentials are setup correctly and that the email service is working as expected. Additionally, there is a docker-volume utility that will confirm your volumes are configured and shared correctly between the front and backend of the application.
The site settings page contains general information about your installation like the application version, some configuration details, and some utilities to help you confirm your installation is working as expected. For example, you can use the Email Configuration section to validate that your email credentials are setup correctly and that the email service is working as expected. Additionally, there is a docker-volume utility that will confirm your volumes are configured and shared correctly between the front and backend of the application.
[Demo](https://beta.mealie.io/admin/site-settings){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/admin/site-settings){ .md-button .md-button--primary }
### Users and Group
There is a small management area for users and groups that allows you to create, edit, and delete users and groups.
[Demo](https://beta.mealie.io/admin/manage/users){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/admin/manage/users){ .md-button .md-button--primary }
### Backups
The backups page provides a full system backup of your installation including all assets and images related to recipes. These are archived into a zip file and stored on the server but can also be downloaded through the UI. Due to some issues in the past Mealie no longer performs automatic backups, **it is advised that during setup you also setup a backup strategy to ensure your data is not lost.**
[Demo](https://beta.mealie.io/admin/backups){ .md-button .md-button--primary }
[Demo](https://demo.mealie.io/admin/backups){ .md-button .md-button--primary }
!!! note
This is **NOT** the same as backups in v0.5.4. We've greatly simplified how backups are managed at the database level and we are now taking a full snapshot of the database and restoring it. If you're looking to export your recipes to move to an alternative service, this is likely not the way you'll want to export that data. You'll likely want to handle that through the group data exports page or through the API itself.
[Group Data Exports](https://beta.mealie.io/group/data/recipes){ .md-button .md-button--primary }
[Group Data Exports](https://demo.mealie.io/group/data/recipes){ .md-button .md-button--primary }

View File

@@ -18,7 +18,12 @@
| ALLOW_SIGNUP | true | Allow user sign-up without token (should match frontend env) |
### Security
| Variables | Default | Description |
| --------------------------- | :-----: | ----------------------------------------------------------------------------------- |
| SECURITY_MAX_LOGIN_ATTEMPTS | 5 | Maximum times a user can provide an invalid password before their account is locked |
| SECURITY_USER_LOCKOUT_TIME | 24 | Time in hours for how long a users account is locked |
### Database
@@ -39,7 +44,7 @@
| SMTP_HOST | None | Required For email |
| SMTP_PORT | 587 | Required For email |
| SMTP_FROM_NAME | Mealie | Required For email |
| SMTP_AUTH_STRATEGY | TLS | Required For email, Options: 'TLS', 'SSL', 'NONE' |
| SMTP_AUTH_STRATEGY | TLS | Required For email, Options: 'TLS', 'SSL', 'NONE' |
| SMTP_FROM_EMAIL | None | Required For email |
| SMTP_USER | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' |
| SMTP_PASSWORD | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' |
@@ -49,6 +54,7 @@ Changing the webworker settings may cause unforeseen memory leak issues with Mea
| Variables | Default | Description |
| ---------------- | :-----: | --------------------------------------------------------------------------------------------------------------------------------- |
| WEB_GUNICORN | false | Enables Gunicorn to manage Uvicorn web for multiple works |
| WORKERS_PER_CORE | 1 | Set the number of workers to the number of CPU cores multiplied by this value (Value \* CPUs). More info [here][workers_per_core] |
| MAX_WORKERS | 1 | Set the maximum number of workers to use. Default is not set meaning unlimited. More info [here][max_workers] |
| WEB_CONCURRENCY | 1 | Override the automatic definition of number of workers. More info [here][web_concurrency] |
@@ -56,9 +62,12 @@ Changing the webworker settings may cause unforeseen memory leak issues with Mea
### LDAP
| Variables | Default | Description |
| ------------------ | :-----: | ------------------------------------------------------------------------------------------------------------------ |
| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth |
| LDAP_SERVER_URL | None | LDAP server URL (e.g. ldap://ldap.example.com) |
| LDAP_BIND_TEMPLATE | None | Templated DN for users, `{}` will be replaced with the username (e.g. `cn={},dc=example,dc=com`) |
| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) |
| Variables | Default | Description |
| ------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------ |
| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth |
| LDAP_SERVER_URL | None | LDAP server URL (e.g. ldap://ldap.example.com) |
| LDAP_TLS_INSECURE | False | Do not verify server certificate when using secure LDAP |
| LDAP_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
| LDAP_BIND_TEMPLATE | None | Templated DN for users, `{}` will be replaced with the username (e.g. `cn={},dc=example,dc=com`, `{}@example.com`) |
| LDAP_BASE_DN | None | Starting point when searching for users authentication (e.g. `CN=Users,DC=xx,DC=yy,DC=de`) |
| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) |

View File

@@ -20,10 +20,10 @@ Setting the following environmental variables will change the theme of the front
| THEME_LIGHT_INFO | #1976D2 | Light Theme Config Variable |
| THEME_LIGHT_WARNING | #FF6D00 | Light Theme Config Variable |
| THEME_LIGHT_ERROR | #EF5350 | Light Theme Config Variable |
| DARK_LIGHT_PRIMARY | #E58325 | Dark Theme Config Variable |
| DARK_LIGHT_ACCENT | #007A99 | Dark Theme Config Variable |
| DARK_LIGHT_SECONDARY | #973542 | Dark Theme Config Variable |
| DARK_LIGHT_SUCCESS | #43A047 | Dark Theme Config Variable |
| DARK_LIGHT_INFO | #1976D2 | Dark Theme Config Variable |
| DARK_LIGHT_WARNING | #FF6D00 | Dark Theme Config Variable |
| DARK_LIGHT_ERROR | #EF5350 | Dark Theme Config Variable |
| THEME_DARK_PRIMARY | #E58325 | Dark Theme Config Variable |
| THEME_DARK_ACCENT | #007A99 | Dark Theme Config Variable |
| THEME_DARK_SECONDARY | #973542 | Dark Theme Config Variable |
| THEME_DARK_SUCCESS | #43A047 | Dark Theme Config Variable |
| THEME_DARK_INFO | #1976D2 | Dark Theme Config Variable |
| THEME_DARK_WARNING | #FF6D00 | Dark Theme Config Variable |
| THEME_DARK_ERROR | #EF5350 | Dark Theme Config Variable |

View File

@@ -63,7 +63,7 @@ After you've configured your database, and updated the `docker-compose.yaml` fil
$ docker-compose up -d
```
You should see the containers start up without error. You should now be able to access the Mealie frontend at [http://localhost:9925](http://locahost:9925).
You should see the containers start up without error. You should now be able to access the Mealie frontend at [http://localhost:9925](http://localhost:9925).
!!! tip "Default Credentials"
@@ -102,8 +102,8 @@ While the docker-compose file should work without modification, some users want
![Docker Diagram](../../../assets/img/docker-diagram.drawio.svg)
In the diagram above there's a few crutial things to note.
In the diagram above there's a few crucial things to note.
1. Port 9925 is the host port, this can be anything you want. The important part is that it's mapped to the mealie-frontend container at port 3000.
2. The mealie-frontend container communicated with the mealie-api container through the INTERNAL docker network. This requires that the two containers are on the same network and that the network supports name resolution (anything but the default bridge network). The resolution URL can be specified in the docker-compose as the `API_URL` environment variable.
3. The mealie-data volume is mounted to BOTH the mealie-frontend and mealie-api containers. This is REQUIRED to ensure that images and assets are severed up correctly. While the default configuration is a docker-volume, that same can be accomplished by using a local directory mounted to the containers.
3. The mealie-data volume is mounted to BOTH the mealie-frontend and mealie-api containers. This is REQUIRED to ensure that images and assets are served up correctly. While the default configuration is a docker-volume, that same can be accomplished by using a local directory mounted to the containers.

View File

@@ -2,15 +2,15 @@
**For Environmental Variable Configuration See:**
- [Frontend Configuration](/mealie/documentation/getting-started/installation/frontend-config/)
- [Backend Configuration](/mealie/documentation/getting-started/installation/backend-config/)
- [Frontend Configuration](./frontend-config.md)
- [Backend Configuration](./backend-config.md)
```yaml
---
version: "3.7"
services:
mealie-frontend:
image: hkotel/mealie:frontend-v1.0.0beta-3
image: hkotel/mealie:frontend-v1.0.0beta-4
container_name: mealie-frontend
depends_on:
- mealie-api
@@ -23,8 +23,12 @@ services:
volumes:
- mealie-data:/app/data/ # (3)
mealie-api:
image: hkotel/mealie:api-v1.0.0beta-3
image: hkotel/mealie:api-v1.0.0beta-4
container_name: mealie-api
deploy:
resources:
limits:
memory: 1000M # (4)
depends_on:
- postgres
volumes:
@@ -66,3 +70,4 @@ volumes:
<br/> <br/> **Note** that both containers must be on the same docker-network for this to work.
2. To access the mealie interface you only need to expose port 3000 on the mealie-frontend container. Here we expose port 9925 on the host, feel free to change this to any port you like.
3. Mounting the data directory to the frontend is now required to access the images/assets directory. This can be mounted read-only. Internally the frontend containers runs a Caddy proxy server that serves the assets requested to reduce load on the backend API.
4. Setting an explicit memory limit is recommended. Python can pre-allocate larger amounts of memory than is necessary if you have a machine with a lot of RAM. This can cause the container to idle at a high memory usage. Setting a memory limit will improve idle performance.

View File

@@ -4,15 +4,15 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
**For Environmental Variable Configuration See:**
- [Frontend Configuration](/mealie/documentation/getting-started/installation/frontend-config/)
- [Backend Configuration](/mealie/documentation/getting-started/installation/backend-config/)
- [Frontend Configuration](./frontend-config.md)
- [Backend Configuration](./backend-config.md)
```yaml
---
version: "3.7"
services:
mealie-frontend:
image: hkotel/mealie:frontend-v1.0.0beta-3
image: hkotel/mealie:frontend-v1.0.0beta-4
container_name: mealie-frontend
environment:
# Set Frontend ENV Variables Here
@@ -23,8 +23,12 @@ services:
volumes:
- mealie-data:/app/data/ # (3)
mealie-api:
image: hkotel/mealie:api-v1.0.0beta-3
image: hkotel/mealie:api-v1.0.0beta-4
container_name: mealie-api
deploy:
resources:
limits:
memory: 1000M # (4)
volumes:
- mealie-data:/app/data/
environment:
@@ -49,3 +53,4 @@ volumes:
<br/> <br/> **Note** that both containers must be on the same docker-network for this to work.
2. To access the mealie interface you only need to expose port 3000 on the mealie-frontend container. Here we expose port 9925 on the host, feel free to change this to any port you like.
3. Mounting the data directory to the frontend is now required to access the images/assets directory. This can be mounted read-only. Internally the frontend containers runs a Caddy proxy server that serves the assets requested to reduce load on the backend API.
4. Setting an explicit memory limit is recommended. Python can pre-allocate larger amounts of memory than is necessary if you have a machine with a lot of RAM. This can cause the container to idle at a high memory usage. Setting a memory limit will improve idle performance.

View File

@@ -1,7 +1,7 @@
# About The Project
!!! warning "Mealie v1 Beta Release"
This documentation if for the Mealie v1 Beta release and is not final. As such, it may contain incomplete or incorrect information. You should understand that installing Mealie v1 Beta is a work in progress and while we've committed to maintaining the database schema and provided migrations, we are still in the process of adding new features, and robust testing to ensure the application works as expected.
This documentation is for the Mealie v1 Beta release and is not final. As such, it may contain incomplete or incorrect information. You should understand that installing Mealie v1 Beta is a work in progress and while we've committed to maintaining the database schema and provided migrations, we are still in the process of adding new features, and robust testing to ensure the application works as expected.
You should likely find bugs, errors, and unfinished pages within the application. To find the current status of the release you can checkout the [project on github](https://github.com/hay-kot/mealie/projects/7) or reach out on discord.

View File

@@ -33,7 +33,7 @@ This can be a plus or a minus depending on your use case. If you relied on the o
## Step 1: Setting Up The New Application
Given the nature of the upgrade, it is highly recommended that you standup a new instance of mealie along side your current instance. This will allow you to migrate your data safely and quickly without any issues. Follow the instructions in the [Installation Checklist](../getting-started/installation/installation-checklist.md) to get started. Once that's complete and you can login, continue here with step 2.
Given the nature of the upgrade, it is highly recommended that you stand up a new instance of mealie along side your current instance. This will allow you to migrate your data safely and quickly without any issues. Follow the instructions in the [Installation Checklist](../getting-started/installation/installation-checklist.md) to get started. Once that's complete and you can login, continue here with step 2.
## Step 2: Exporting Your Data from Pre-v1
@@ -42,7 +42,7 @@ In your instance of Mealie prior to v1, perform an export of your data in the Ad
## Step 3: Using the Migration Tool
In your new v1 instance, navigate to `/group/migrations` and select "Mealie" from the dropdown selector. Upload the exported data from your pre-v1 instance. Optionally, you can tag all the recipes you've imported with the `mealie_alpha` tag to help you identify them. Once the upload has finished, submit the form and your migration will begin. This may take some time, but when it's complete you'll be provided a new entry in hte "Previous Migrations" table below. Be sure to review the migration report to make sure everything was successful. There may be instances where some of the recipes were not imported, but the migration will still import all the successful recipes.
In your new v1 instance, navigate to `/group/migrations` and select "Mealie" from the dropdown selector. Upload the exported data from your pre-v1 instance. Optionally, you can tag all the recipes you've imported with the `mealie_alpha` tag to help you identify them. Once the upload has finished, submit the form and your migration will begin. This may take some time, but when it's complete you'll be provided a new entry in the "Previous Migrations" table below. Be sure to review the migration report to make sure everything was successful. There may be instances where some of the recipes were not imported, but the migration will still import all the successful recipes.
In most cases, it's faster to manually migrate the recipes that didn't take instead of trying to identify why the recipes failed to import. If you're experiencing issues with the migration tool, please open an issue on GitHub.

View File

@@ -0,0 +1,37 @@
# Backups and Restoring
Mealie provides an integrated mechanics for doing full installation backups of the database. Navigate to `/admin/backups` to
- See a list of available backups
- Perform a backups
- Restore a backup
!!! tip
If you're using Mealie with SQLite all your data is stored in the /app/data/ folder in the container. You can easily perform entire site backups by stopping the container, and backing up this folder with your chosen tool. This is the **best** way to backup your data.
## Restoring from a Backup
To restore from a backup it needs to be uploaded to your instance, this can be done through the web portal. On the lower left hand corner of the backups data table you'll see an upload button. Click this button and select the backup file you want to upload and it will be available to import shortly.
Before importing it's critical that you understand the following:
- This is a destructive action and will delete all data in the database
- This action cannot be undone
- If this action is successful you will be logged out and you will need to log back in to complete the restore
!!! warning
Prior to beta-v5 using a mis-matched version of the database backup will result in an error that will prevent you from using the instance of Mealie requiring you to remove all data and reinstall. Post beta-v5 performing a mismatched restore will throw an error and alert the user of the issue.
### Postgres Note
Restoring the Database when using Postgres requires Mealie to be configured with a postgres **superuser** account. This is due to our usage of massive deleting of data in the database and temporarily setting roles to perform the restore. To perform a restoration on Postgres you will need to _temporarily_ set the Mealie user to a superuser account.
```sql
ALTER USER mealie WITH SUPERUSER;
# Run restore from Mealie
ALTER USER mealie WITH NOSUPERUSER;
```
For more information see [GitHub Issue #1500](https://github.com/hay-kot/mealie/issues/1500)

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
site_name: Mealie
#demo_url: https://mealie-demo.hay-kot.dev/
demo_url: https://demo.mealie.io
site_url: https://hay-kot.github.io/mealie/
use_directory_urls: true
theme:
@@ -69,6 +69,8 @@ nav:
- PostgreSQL: "documentation/getting-started/installation/postgres.md"
- Frontend Configuration: "documentation/getting-started/installation/frontend-config.md"
- Backend Configuration: "documentation/getting-started/installation/backend-config.md"
- Usage:
- Backup and Restoring: "documentation/getting-started/usage/backups-and-restoring.md"
- Community Guides:
- iOS Shortcuts: "documentation/community-guide/ios.md"
@@ -88,6 +90,7 @@ nav:
- Improving Ingredient Parser: "contributors/guides/ingredient-parser.md"
- Change Log:
- v1.0.0beta-4: "changelog/v1.0.0beta-4.md"
- v1.0.0beta-3: "changelog/v1.0.0beta-3.md"
- v1.0.0beta-2: "changelog/v1.0.0beta-2.md"
- v1.0.0 Beta: "changelog/v1.0.0.md"

View File

@@ -52,7 +52,10 @@ module.exports = {
"ts-ignore": "allow-with-description",
},
],
"no-restricted-imports": ["error", { paths: ["@vue/reactivity", "@vue/runtime-dom", "@vue/composition-api"] }],
"no-restricted-imports": [
"error",
{ paths: ["@vue/reactivity", "@vue/runtime-dom", "@vue/composition-api", "vue-demi"] },
],
// TODO Gradually activate all rules
"@typescript-eslint/no-unsafe-assignment": "off",

View File

@@ -1 +0,0 @@
_

View File

@@ -36,7 +36,7 @@
handle @apidocs {
uri strip_suffix /
reverse_proxy http://mealie-api:9000
reverse_proxy {$API_URL}
}
handle {

View File

@@ -1,14 +0,0 @@
import { BaseCRUDAPI } from "../_base";
import { UserIn, UserOut } from "~/types/api-types/user";
const prefix = "/api";
const routes = {
adminUsers: `${prefix}/admin/users`,
adminUsersId: (tag: string) => `${prefix}/admin/users/${tag}`,
};
export class AdminUsersApi extends BaseCRUDAPI<UserIn, UserOut, UserOut> {
baseRoute: string = routes.adminUsers;
itemRoute = routes.adminUsersId;
}

View File

@@ -1,12 +0,0 @@
import { ValidatorsApi } from "./public/validators";
import { ApiRequestInstance } from "~/types/api";
export class PublicApi {
public validators: ValidatorsApi;
constructor(requests: ApiRequestInstance) {
this.validators = new ValidatorsApi(requests);
Object.freeze(this);
}
}

View File

@@ -0,0 +1,378 @@
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-cyrillic-ext1.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-cyrillic2.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-greek-ext3.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-greek4.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-vietnamese5.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-latin-ext6.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-latin7.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-cyrillic-ext8.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-cyrillic9.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-greek-ext10.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-greek11.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-vietnamese12.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-latin-ext13.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-latin14.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-cyrillic-ext15.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-cyrillic16.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-greek-ext17.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-greek18.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-vietnamese19.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-latin-ext20.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-latin21.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-cyrillic-ext22.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-cyrillic23.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-greek-ext24.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-greek25.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-vietnamese26.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-latin-ext27.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-latin28.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-cyrillic-ext29.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-cyrillic30.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-greek-ext31.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-greek32.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-vietnamese33.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-latin-ext34.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-latin35.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-cyrillic-ext36.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-cyrillic37.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-greek-ext38.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-greek39.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-vietnamese40.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-latin-ext41.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-latin42.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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