Compare commits

...

385 Commits

Author SHA1 Message Date
Michael Genson
b153ddf858 feat: more shopping list enhancements (#2587)
* fix new position calculataion

* ensure consistent list item ordering

* fix recipe ref overflow on small screens

* added recipe ref elevation

* tweaked line height (for long notes)

* removed unused user dependency

* remove old shopping list items when there's >100

* 🤷

* cleaned up function generator

* fixed test

* fix potential type error

* made max position calc more efficient
2023-10-07 13:06:00 -08:00
Hayden
f35bc77a7d New Crowdin updates (#2600)
* New translations en-us.json (Romanian)

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

* New translations en-us.json (Italian)

* New translations en-us.json (Japanese)

* New translations en-us.json (Korean)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Dutch)

* New translations en-us.json (Norwegian)

* New translations en-us.json (Polish)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

* New translations en-us.json (Slovak)

* New translations en-us.json (Slovenian)

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

* New translations en-us.json (Swedish)

* New translations en-us.json (Turkish)

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

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

* New translations en-us.json (Croatian)

* New translations en-us.json (Latvian)

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

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

* New translations en-us.json (Arabic)

* New translations en-us.json (Danish)

* New translations en-us.json (Finnish)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

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

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

* New translations en-us.json (Finnish)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

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

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

* New translations en-us.json (Romanian)

* New translations en-us.json (Arabic)

* New translations en-us.json (Danish)

* New translations en-us.json (Finnish)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

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

* New translations en-us.json (Romanian)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Russian)

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

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

* New translations en-us.json (German)
2023-10-07 13:05:39 -08:00
Hayden
db021023fb chore:bump dependencies and fix errors (#2601)
* bump dependencies and fix errors

* fix depreciated arg

* properly handle validation errors
2023-10-07 13:02:15 -08:00
Johan Lindell
954a2f5113 fix: "remember me" with long TOKEN_TIME (#2602)
* Fixed "remember me" with long TOKEN_TIME

* Reverted changes in create_access_token
2023-10-07 20:58:45 +00:00
Arsène Reymond
45022e1f1b fixes: PWA & interface on small screens (#2536)
* fix pwa icons

* fix spacing and layouts for small screens

* translate missing strings

* Revert "translate missing strings"

This reverts commit 150a961a08.

* fixes

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-10-07 11:36:47 -08:00
Michael Genson
bd79c1db2f chore: Get Rid of Warnings (#2599)
* ignore unsafe html warnings

* remove unused import

* re-order attrs

* removed unused vars/imports

* removed unused import

* refactored v-html

* removed more unused things
2023-10-07 11:23:47 -08:00
Michael Genson
b86c4e5865 feat: Removed Cross-Version Disclaimer from Backup Page (#2597)
* removed cross-version disclaimer from backup text

* grammar tweak
2023-10-07 11:23:26 -08:00
Michael Genson
a98e863bca fix: database unique constraints (#2594)
* fixed missing migration name

* added unique constraints to all m2m tables

* fixed bug trying to create duplicate tags

* added more unique constraints

* fixed duplicate seeder data

* updated tests

* fixed seed rollback error
2023-10-07 11:23:13 -08:00
Michael Genson
247a4de283 docs: fix old mealplanner route ref (#2590) 2023-10-07 11:21:46 -08:00
Michael Genson
26ef351ae7 fix: Recipe Zip Export Can't Be Imported (#2585)
* clean recipe data when importing via zip

* added tests

* simplified recursive logic
2023-10-07 11:18:55 -08:00
Johan Lindell
84b477edf6 fix: removed unused node in docker image (#2572) 2023-10-07 11:17:26 -08:00
Simon Becker
a331042e6d build: optimize apt-get cleanup during image build (#2558) 2023-09-29 16:04:58 -08:00
Johan Lindell
40ab328bb9 fix: Make sure recipe state is updated correctly (#2566) 2023-09-29 15:59:27 -08:00
Michael Genson
b32e2f1bf7 fix: Recipe Card Section Infinite Loop (#2584)
* refactored recipe card section fetch

* minor optimization

* lint
2023-09-29 15:59:07 -08:00
Michael Genson
1074cad5dc feat: disable admin option when using LDAP auth (#2583)
* fix typo

* add override readonly/disable support for autoform

* made admin permission conditionally disabled
2023-09-29 15:58:34 -08:00
Matthew Hill
4bd7bda60d fix: Fix bugs with account locking (#2580)
* fix(security): reset login attempts after successful login

Enforce a maximum number of consecutive failed logins. Successfully logging in should reset the
count.

#2569

* fix(security): fix when user is unlocked

The user should be unlocked when locked_at is set, but the lock has expired.

#2569
2023-09-29 15:58:00 -08:00
Michael Genson
484c60c7ea docs: spelling, content updates for clarity (#2581) 2023-09-28 15:17:10 -08:00
Michael Genson
ef805c4b57 fix: replace default enter key behavior (#2582) 2023-09-28 15:16:22 -08:00
Hayden
222d583d6f docs: add note on migration(#2576) 2023-09-26 07:14:48 -08:00
Jacob Corn
b7a6bff5aa docs: add tag to postgres image (#2575) 2023-09-26 06:35:23 -08:00
Flightkick
bb9afd86c1 fix: Use reserved example.com as bogus instead of email.com domain. (#2551)
`email.com` is not a reserved domain, incorrect configuration could result in unintentional effects.
`example.com` is reserved by IANA for bogus purposes, see RFC 6761.
2023-09-23 07:56:34 -08:00
Michael Genson
a22d500603 fix: CSS rendering bug, again (#2555)
* idk man

* that wasn't supposed to be there
2023-09-19 09:07:08 -08:00
Michael Genson
3e5596f898 fix: Home Doesn't Load For Non-Admin Users (#2556)
* added non-admin route for fetching current group

* simplified frontend group slug fetching

* exposed public link even if user can't invite

* 🧹
2023-09-19 09:06:39 -08:00
Max Bachmann
15c6df88ab perf: use score_cutoff for fuzzy string matching (#2553) 2023-09-16 15:24:45 -05:00
Grygon
9a04b11ee5 fix: #2534 - Add clearable ratings (#2541)
* Add clearable to RecipeRating, Fix #2534

* Make rating nullable

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-09-15 19:10:44 +00:00
Michael Genson
2dfbe9f08d feat: Improved Ingredient Matching (#2535)
* added normalization to foods and units

* changed search to reference new normalized fields

* fix tests

* added parsed food matching to backend

* prevent pagination from ordering when searching

* added extra fuzzy matching to sqlite ing matching

* added tests

* only apply search ordering when order_by is null

* enabled post-search fuzzy matching for postgres

* fixed postgres fuzzy search test

* idk why this is failing

* 🤦

* simplified frontend ing matching
and restored automatic unit creation

* tightened food fuzzy threshold

* change to rapidfuzz

* sped up fuzzy matching with process

* fixed units not matching by abbreviation

* fast return for exact matches

* replace db searching with pure fuzz

* added fuzzy normalization

* tightened unit fuzzy matching thresh

* cleaned up comments/var names

* ran matching logic through the dryer

* oops

* simplified order by application logic
2023-09-15 17:19:34 +00:00
Hayden
084ad4228b remove permissions from nested workflow 2023-09-14 09:42:46 -05:00
Hayden
86b0b6761d add permissions to build-release 2023-09-14 09:42:32 -05:00
Hayden
2ad6af2cce feat: consolidate deployment targets and publish to ghcr.io (#2539)
* WIP: proof of concept

* basic meta tag injection

* add support for scraping public/private links

* make tests go brrrrr

* cleanup initialization

* rewrite build config

* remove recipe meta on frontend

* make type checker happy

* remove other deployment methods

* fix issue with JSON response on un-authenticated request

* docs updates

* update tivy scanner

* fix linter stuff

* change registry tag

* build fixes

* fix same mistake I always make
2023-09-14 06:40:13 -08:00
Michael Genson
aec4cb4f31 feat: Advanced Query Filter Record Ordering (#2530)
* added support for multiple order_by strs

* refactored qf to expose nested attr logic

* added nested attr support to order_by

* added tests

* changed unique user to be function-level

* updated docs

* added support for null handling

* updated docs

* undid fixture changes

* fix leaky tests

* added advanced shopping list item test

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-09-14 14:09:05 +00:00
Michael Genson
2c5e5a8421 feat: Public Recipe Browser (#2525)
* fixed incorrect var ref

* added public recipe pagination route

* refactored frontend public/explore API

* fixed broken public cards

* hid context menu from cards when public

* fixed public app header

* fixed random recipe

* added public food, category, tag, and tool routes

* not sure why I thought that would work

* added public organizer/foods stores

* disabled clicking on tags/categories

* added public link to profile page

* linting

* force a 404 if the group slug is missing or invalid

* oops

* refactored to fit sidebar into explore

* fixed invalid logic for app header

* removed most sidebar options from public

* added backend routes for public cookbooks

* added explore cookbook pages/apis

* codegen

* added backend tests

* lint

* fixes v-for keys

* I do not understand but sure why not

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-09-14 06:01:24 -08:00
Hayden
e28b830cd4 hotfix: format 2023-09-06 23:20:59 -05:00
Matthew Macdonald-Wallace
71f1607b58 chore: switch to workflow actions and upload to GHCR (#2355)
* Switch to workflow actions and upload to GHCR

* cleanup yml file

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-09-06 19:20:42 -08:00
Michael Genson
0a00a6ea0d fix: removed HTML tags when copying recipe ingredients (#2533) 2023-08-31 09:08:44 -08:00
Hugo van Rijswijk
408ca88cb2 fix: ingredient item should be single-line (#2521)
This is an issue on non-chromium browsers. I guess Chromium has some sort of support for nested CSS, whereas other browsers don't.
2023-08-25 08:32:26 -08:00
Michael Genson
30717e50d3 made parsed ingredient reactive (#2520) 2023-08-25 08:32:06 -08:00
Michael Genson
693608d59f feat: cook timer (#2508)
* updated base button group

* added kitchen timer

* added missing icon

* usability tweaks

* for for menu rendering over app bar

* clean up types

* fix for mp3 loading, maybe?

* spooky linter fixes

* for real this time

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-23 09:31:23 -08:00
Michael Genson
5c5432304f fix: recipe timeline UI glitches (#2519)
* fix invalid undefined prop

* fixed weird rendering

* made items pop more against background

* fixed invalid v-if

* fixed indentation
2023-08-23 09:30:59 -08:00
Michael Genson
5213a61d9b fix: logic for disableAmount in shopping list items (#2518) 2023-08-22 14:03:24 -08:00
Michael Genson
ae502c5652 added missing recipe list import (#2516) 2023-08-21 17:30:31 -08:00
Michael Genson
c9cc7a93c8 fix: Tools Shouldn't Be Unique Across Groups (#2505)
* fixed type/abc errors in tests

* fixed false positive multitentant tests

* fix tools not allowing unique slugs across groups

* fixed alembic refs

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-21 20:39:23 +00:00
Arsène Reymond
99e7717fec UI/UX improvements (#2423)
* disable autofocus and hide keyboard on enter

* improve a11y

* fix non-translated string

* improve recipeTimeline UI

* format

* fixes
2023-08-21 18:41:18 +00:00
Michael Genson
1e693fdca6 fix: refresh tag store when new tags are imported (#2504) 2023-08-21 09:18:55 -08:00
Michael Genson
d6e4829e6f feat: Display Shopping List Item Recipe Refs (#2501)
* added recipe ref display to shopping list items

* added backend support for recipe notes

* added recipe note to item recipe ref display

* fixed note merge bug with 3+ notes

* tweak display

* lint

* updated alembic refs

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-21 09:18:37 -08:00
Hugo van Rijswijk
50a92c165c feat: improve readability of ingredients list (#2502)
* feat: improve readability of notes in ingredients list

Makes the notes in the ingredients list more readable by making them slightly opaque. This creates a better visual separation between the notes and the rest of the ingredient.

* Use server display if available

* Move note to newline and make quantity more distinct

* Use safeMarkdown for shopping list

* Use component

* Wrap unit in accent color

* Update RecipeIngredientListItem to set food in bold
2023-08-21 15:32:09 +00:00
Michael Genson
2151451634 feat: Timeline Image Uploader Improvements (#2494)
* improved UI responsiveness and added image preview

* added global image cropper component

* added image cropper to last made dialog

* style tweaks

* added more specific text for creating event

* mopped up some slop

* renamed height and width vars

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-21 15:00:37 +00:00
Michael Genson
e24e28ae03 feat: Improved recipeYield Parsing For Fractions and Decimals (#2507)
* improved recipeYield parsing for fracs/decimals

* added fix for edgecase with weird fractions

* made typescript happy

* lint

* extracted yield calculation into composable

* fixed some gross edgecases

* added tests

* made bare return clearer

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-21 06:54:02 -08:00
Hayden
c60c63852b chore: bump deps (#2513)
* bump dependencies

* run code-generator

* add direct dependency to text-unidecode

* fix dev dependencies group
2023-08-20 16:09:13 -08:00
Michael Genson
095edef95e feat: Improve Public URL Readability (#2482)
* added support for group slugs

* modified frontend to use links with group slug

* fixed test refs

* unused import

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-20 10:38:46 -08:00
Michael Genson
99372aa2b6 feat: Generalize Search to Other Models (#2472)
* generalized search logic to SearchFilter

* added default search behavior for all models

* fix for schema overrides

* added search support to several models

* fix for label search

* tests and fixes

* add config for normalizing characters

* dramatically simplified search tests

* bark bark

* fix normalization bug

* tweaked tests

* maybe this time?

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-20 10:30:21 -08:00
renovate[bot]
76ae0bafc7 fix(deps): update dependency pydantic to v1.10.12 (#2467)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-20 10:28:05 -08:00
renovate[bot]
1aa7344688 fix(deps): update dependency lxml to v4.9.3 (#2465)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-20 10:27:53 -08:00
renovate[bot]
da17f2a410 chore(deps): update dependency pytest-asyncio to v0.21.1 (#2462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-20 10:27:16 -08:00
renovate[bot]
4169c3e743 chore(deps): update dependency pylint to v2.17.5 (#2421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-20 10:27:04 -08:00
renovate[bot]
3ccae9e366 chore(deps): update actions/setup-node action to v3.7.0 (#2468)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-08-20 10:18:12 -08:00
renovate[bot]
26ab73a827 chore(deps): update dependency mkdocs-material to v9.1.21 (#2461)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-20 10:17:00 -08:00
Michael Genson
d8a6f900b4 docs: add windows instructions for the dev container (#2493) 2023-08-15 18:44:17 -08:00
Weetbix
f6e7eba6fc docs: fix typo in FAQ (#2495) 2023-08-15 18:43:43 -08:00
Michael Genson
e55258c5be fx: Nextcloud migration fails to parse null times (#2485)
* support null or custom strings

* made time parsing more robust using isodate

* bark bark
2023-08-09 18:52:49 -08:00
Michael Genson
dd947c316a fix: Tandoor doesn't import all images (#2487)
* use glob to find files for import

* bark bark

* add catch for invalid image files
2023-08-09 18:52:32 -08:00
Michael Genson
04c7e068d8 typo: have have (#2490) 2023-08-09 18:52:00 -08:00
Michael Genson
bdf6b1054e Chore: Rename Omni Build Action (#2483)
* fix omni action name

* added omni to Discord notification text
2023-08-06 19:58:38 -08:00
Michael Genson
dfe4942451 feat: Recipe Timeline Images (#2444)
* refactored recipe image paths/service

* added routes for updating/fetching timeline images

* make generate

* added event image upload and rendering

* switched update to patch to preserve timestamp

* added tests

* tweaked order of requests

* always reload events when opening the timeline

* re-arranged elements to make them look nicer

* delete files when timeline event is deleted
2023-08-06 09:49:30 -08:00
Hayden
06962cf865 New Crowdin updates (#2476)
* New translations en-US.json (Croatian)

* New translations en-US.json (Swedish)
2023-08-06 09:47:19 -08:00
Hayden
3fb60339a6 remove duplicate issues 2023-08-06 12:39:45 -05:00
Hayden
89a6b32a3f fix syntax errors 2023-08-06 12:39:02 -05:00
Hayden
6901747a26 chore: rework issue templates (#2481)
* rewrok issue templates

* fix feature request links

* feature-template
2023-08-06 09:35:38 -08:00
Michael Genson
e71155a55d fix: removed variable rendering on organizers page (#2477) 2023-07-31 08:22:54 -08:00
Hayden
b1eb156145 New Crowdin updates (#2463)
* New translations en-US.json (Spanish)

* New translations en-US.json (Hebrew)

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

* New translations en-US.json (Korean)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Romanian)

* New translations en-US.json (French)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Arabic)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Catalan)

* New translations en-US.json (Czech)

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

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

* New translations en-US.json (Turkish)

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

* New translations en-US.json (Croatian)

* New translations en-US.json (Latvian)

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

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

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

* New translations en-US.json (Turkish)

* New translations en-US.json (French)

* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Catalan)

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

* New translations en-US.json (Swedish)
2023-07-29 13:40:09 -08:00
Michael Genson
e434029bac feat: prevent all changes to the default user in demo (#2470) 2023-07-29 13:39:49 -08:00
TomArm
a75116bc62 docs: updated home assistant docs (#2459)
Co-authored-by: tomarm <a@a.com>
2023-07-23 09:54:49 -08:00
Michael Genson
c86406e027 fix: Omni Container /docs Proxy and PyYaml dependency (#2457)
* upgrade pyyaml to ^6.0.1

* add proxy routes for docs
2023-07-23 09:53:45 -08:00
Michael Genson
0f896107f9 feat: Migrate from Tandoor (#2438)
* added tandoor migration to backend

* added tandoor migration to frontend

* updated tests

* ignore 0 amounts

* refactored ingredient display calculation

* fix parsing tandoor recipes with optional data

* generated frontend types

* fixed inconsistent default handling and from_orm

* removed unused imports
2023-07-23 09:52:09 -08:00
Michael Genson
c25b58e404 fix: Missing Paprika and Nextcloud Migration Data (#2434)
* fixed paprika url key

* fixed paprika total and prep time aliases

* added nextcloud time parsing

* mapped paprika categories to tags

* cleaned up netcloud parsetime
2023-07-23 09:50:47 -08:00
Carter
c783d86a2a feat: LDAP attribute validation (#2400)
* validate user attributes on user creation

add logs for invalid or missing attributes

* only update admin flag when admin status changes

* move ldap functions into separate file

* fix linter issues

* actually use the search_user function

* fix types
2023-07-23 09:49:24 -08:00
Hayden
e64265615e New Crowdin updates (#2410)
* New translations en-US.json (Greek)

* New translations en-US.json (Greek)

* New translations en-US.json (Greek)

* New translations en-US.json (Slovak)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

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

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

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Galician)

* New translations en-US.json (Galician)

* New translations en-US.json (Galician)

* New translations en-US.json (Galician)

* New translations en-US.json (Galician)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Latvian)

* New translations en-US.json (Latvian)

* New translations en-US.json (Latvian)

* New translations en-US.json (Latvian)

* New translations en-US.json (Latvian)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Croatian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

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

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

* New translations en-US.json (Spanish)

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

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

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

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

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Hebrew)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* New translations en-US.json (Korean)

* New translations en-US.json (Korean)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)
2023-07-23 09:48:35 -08:00
renovate[bot]
9f1f87bcd0 chore(deps): update dependency rich to v13.4.2 (#2382)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-23 09:47:47 -08:00
renovate[bot]
a7d8fc8d25 chore(deps): update dependency coverage to v7.2.7 (#2341)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-23 09:47:26 -08:00
renovate[bot]
8584ca666f fix(deps): update dependency pillow to v9.5.0 (#2339)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-23 09:47:12 -08:00
renovate[bot]
50a06f3553 fix(deps): update dependency core-js to v3.31.1 (#2338)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-23 09:46:54 -08:00
renovate[bot]
9f9bdec886 fix(deps): update dependency beautifulsoup4 to v4.12.2 (#2337)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-23 09:46:38 -08:00
renovate[bot]
e3652a0876 fix(deps): update dependency sqlalchemy to v2.0.19 (#2336)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-23 09:46:23 -08:00
Carter
4af9eec89d fix omni docker image (#2432) 2023-06-27 22:00:19 -08:00
Carter
d1b06e7339 fix: Change libldap-2.4-2 to libldap-common in docker (#2429) 2023-06-27 17:25:41 -08:00
Jacob Corn
9557b3f0b6 fix: log accurate IP (#2416)
* update dev docker poetry install

* Forward/Report IP through front and backend.

* Add fail2ban docs

* fix option name and iproute2 in omni entry

* Fix entry scripts -> gunicorn setting respected

* gunicorn off

* xfwd in nuxt proxy and handle multiple IPs
2023-06-25 10:22:21 -08:00
Carter
5e904d19b4 docs: re-add and fix the bookmarklet instructions to docs (#2419) 2023-06-20 22:09:20 -08:00
Carter
eea38a5b1e fix: url format in the recipe chip (#2420) 2023-06-20 22:08:47 -08:00
Hayden
674774d073 New Crowdin updates (#2387)
* New translations en-US.json (Norwegian)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (French)
2023-05-29 16:56:38 -08:00
Jacob Corn
e1d3a247c7 feat: random sort option for front page (#2363)
* Add hook for random sorting

* Add random sorting to front page

* Add multiple tests for random sorting.

* Be extra sure that all recipes are returned.

* Too stable random. seed doesn't reach backend.

* add timestamp to useRecipeSearch

* Update randomization tests for timestamp seeding

* ruff cleanup

* pass timestamp separately in getAll

* remove debugging log items

* remove timestamp from address bar

* remove defaults from backend timestamps

* timestamp should be optional

* fix edge case: query without timestamp

* similar edge case: no timestamp in pagination

* ruff :/

* better edge case handling

* stabilize random search test w/more recipes

* better pagination seeding

* update pagination seed test

* remove redundant random/seed check

* Test for api routes to random sorting.

* please the typing gods

* hack to make query parameters throw correct exc

* ruff

* fix validator message typo

* black reformatting

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-05-29 16:56:20 -08:00
Jacob Corn
7e0d29afc7 feat: search tokenization, handling of quoted literal search, and postgres fuzziness (#2351)
* Creating postgres migration script and starting to set up to detect database

* non-working placeholders for postgres pg_tgrm

* First draft of some indexes

* non-working commit of postgres indexing

* Further non-working edits to db-centric fuzzy search

* update alembic for extensions

* More non-working setup

* Move db type check to init_db

* fix typo in db name check

* Add sqlite token search and postgres full text search

* reorder search to hit exact matches faster

* Add settings and docs for POSTGRES_LANGUAGE (full text search)

* Use user-specified POSTGRES_LANGUAGE in search

* fix fuzzy search typo

* Remove full text search and instead order by trigram match

* cleaner adding of indices, remove fulltext

* Cleanup old import of getting app settings

* Fix typo in index

* Fix some alembic fuzzy typos

* Remove diagnostic printing from alembic migration

* Fix mixed up commutator for trigram operator and relax criteria

* forgot to remove query debug

* sort only on name

* token and fuzzy search tests

* Refactor recipe search test to avoid rare random string cross-matches.

* Add ability to quote parts of search for exact match

* Remove internal punctuation, unless it's quoted for literal search

* Add tests for special character removal and literal search

* Remove the outer double quotes from searches, but leave internal single quotes alone.

* Update tests to avoid intra-test name collisions

* Fixing leftovers highlighted by lint

* cleanup linting and mypy errors

* Fix test cross-matching on dirty db (leftovers from bulk import)

* forgot to cleanup something when debugging mypy errors

* re-order pg_trgm loading in postgres

* address comments
2023-05-28 09:46:53 -08:00
Jacob Corn
27ebb4c462 docs: update dev docs, fix broken links, improve clarity of key points (#2354)
* Dev docs: tests, postgres/psycog2

* Update pull request process.

* Add Food/Unit parsing instructions to the FAQ

* Update docker composes: mealie-data now local to docker-compose rather than hidden in docker volume dir! postgres points to 1.0.0b5

* sqlite docker-compose: mealie-data now local rather than hidden in docker volumes

* Merge Intro FAQ into main FAQ

* Progress on docs

* Add Advanced and v1b5 to docs index

* v1b5 changelog consistency with other changelogs

* Features: fix wrong link, name buttons for clarity

* Migration: link to github releases

* Updating: link to migration page, format docker cmds

* FAQ: update smart ingredient formatting

* Intro: fix typos

* API: update for newbie clarity

* Roadmap: update feature request & progress mechanism

* iOS shortcut: fix broken image links

* installation: add SMTP google app passwords

* Postgres: add header note on why.

* Update Groups doc per Discord discussion

* mealie-data back into docker default volume path
2023-05-13 10:50:22 -08:00
Michael Genson
8e2d50054c Fix: Query Filter Date Comparisons Are Off By One Date (#2389)
* fixed erroneous date -> datetime conversion

* added tests for date and datetime bounds
2023-05-11 22:28:14 -08:00
Michael Genson
5d87b7e411 feature: query filter support for common SQL keywords (#2366)
* added support for SQL keywords IS, IN, LIKE, NOT
deprecated datetime workaround for "<> null"
updated frontend reference for "<> null" to "IS NOT NULL"

* tests

* refactored query filtering to leverage orm

* added CONTAINS ALL keyword

* tests

* fixed bug where "and" or "or" was in an attr name

* more tests

* linter fixes

* TIL this works
2023-05-06 14:28:40 -08:00
renovate[bot]
9b726126ed chore(deps): update dependency pylint to v2.17.3 (#2381)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-06 10:03:04 -08:00
renovate[bot]
c6346ed2a5 chore(deps): update dependency mkdocs-material to v9.1.9 (#2379)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-06 10:02:50 -08:00
Michael Genson
20a78677ef fix: Potential Fix for Global Timeline Server Error (#2372)
* simplified group id logic

* moved onscroll listener to on-mounted
2023-05-06 10:01:38 -08:00
Hayden
23786c1f5e bump ruff and do fixes (#2375) 2023-05-05 14:43:23 -08:00
Michael Genson
f27bb00dc3 fix: remove old prop reference (#2367) 2023-05-05 14:17:29 -08:00
Jacob Corn
2e43b51882 Add failed login & IP to log (#2365)
* Add failed login & IP to log

* Small change for human readability/intuitiveness
2023-05-05 14:15:55 -08:00
Hayden
a2e69b5565 New Crowdin updates (#2340)
* New translations en-US.json (Danish)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (German)

* New translations en-US.json (Swedish)

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

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

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Finnish)

* New translations en-US.json (Finnish)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Finnish)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Finnish)

* New translations en-US.json (Italian)

* New translations en-US.json (Swedish)

* New translations en-US.json (German)

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

* New translations en-US.json (Japanese)

* New translations en-US.json (Finnish)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (Danish)

* New translations en-US.json (Greek)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Korean)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Turkish)

* 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 (English, United Kingdom)

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

* New translations en-US.json (German)

* New translations en-US.json (Czech)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Czech)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

* New translations en-US.json (Turkish)

* New translations en-US.json (Russian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Finnish)

* New translations en-US.json (Romanian)

* New translations en-US.json (Czech)

* New translations en-US.json (Italian)
2023-05-05 14:11:53 -08:00
Michael Genson
75698c531a fix: Shopping List Label Dropdown Doesn't Save Correctly (#2361)
* only update items by label on refresh

* made changes more responsive

* fast re-order items when labels are re-ordered
2023-04-25 09:46:58 -08:00
Michael Genson
fe17922bb8 Feature: Global Timeline (#2265)
* extended query filter to accept nested tables

* decoupled timeline api from recipe slug

* modified frontend to use simplified events api

* fixed nested loop index ghosting

* updated existing tests

* gave mypy a snack

* added tests for nested queries

* fixed "last made" render error

* decoupled recipe timeline from dialog

* removed unused props

* tweaked recipe get_all to accept ids

* created group global timeline
added new timeline page to sidebar
reformatted the recipe timeline
added vertical option to recipe card mobile

* extracted timeline item into its own component

* fixed apploader centering

* added paginated scrolling to recipe timeline

* added sort direction config
fixed infinite scroll on dialog
fixed hasMore var not resetting during instantiation

* added sort direction to user preferences

* updated API docs with new query filter feature

* better error tracing

* fix for recipe not found response

* simplified recipe crud route for slug/id
added test for fetching by slug/id

* made query filter UUID validation clearer

* moved timeline menu option below shopping lists

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-04-25 09:46:00 -08:00
Michael Genson
0e397b34fd fix: broken frontend development (#2357)
* dependency hell be like

* moved resolution to subdependency only
2023-04-25 09:44:48 -08:00
renovate[bot]
fa59c1eb0b fix(deps): update dependency apprise to v1.3.0 (#2334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-05 18:07:13 -08:00
renovate[bot]
998029da6f fix(deps): update dependency alembic to v1.10.3 (#2333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-05 18:06:55 -08:00
renovate[bot]
d117b4075b chore(deps): update dependency pylint to v2.17.2 (#2332)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-05 18:06:13 -08:00
Hayden
e8de70c894 New Crowdin updates (#2331)
* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)
2023-04-05 18:05:50 -08:00
renovate[bot]
16b47a3c10 chore(deps): update dependency pre-commit to v3.2.2 (#2330)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-05 18:05:22 -08:00
renovate[bot]
05a7047ae3 chore(deps): update dependency eslint to v8.37.0 (#2329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-05 18:04:43 -08:00
renovate[bot]
de4debe749 fix(deps): update dependency psycopg2-binary to v2.9.6 (#2320)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 11:56:17 -08:00
renovate[bot]
fcdb11d8a1 chore(deps): update dependency black to v23.3.0 (#2327)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 11:56:05 -08:00
renovate[bot]
6c625e402c fix(deps): update dependency recipe-scrapers to v14.36.0 (#2326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 11:55:37 -08:00
Hayden
750072344d New Crowdin updates (#2319)
* New translations en-US.json (French)

* New translations en-US.json (Slovak)

* New translations en-US.json (Swedish)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)
2023-04-04 11:29:29 -08:00
renovate[bot]
d735f1c0d8 fix(deps): update dependency sqlalchemy to v2.0.8 (#2322)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 11:29:02 -08:00
renovate[bot]
29956142e1 chore(deps): update dependency pytest-asyncio to ^0.21.0 (#2290)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 11:28:30 -08:00
renovate[bot]
0ff46cda92 chore(deps): update dependency eslint-plugin-vue to v9.10.0 (#2289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 11:28:13 -08:00
renovate[bot]
4e251b3092 fix(deps): update dependency recipe-scrapers to v14.35.0 (#2318)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-04 11:26:44 -08:00
Carter
10730bfa77 [Fix] Filter out the LDAP entries which do not have a DN (#2288)
* use first returned ldap entry

* set OPT_REFERRALS to 0

* filter out ldap entries not having a dn
2023-04-04 09:22:47 -08:00
renovate[bot]
03b7d9e200 fix(deps): update dependency orjson to v3.8.9 (#2315)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-03 10:06:28 -08:00
renovate[bot]
8a683e7344 chore(deps): update dependency vitest to v0.29.8 (#2314)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-03 10:06:14 -08:00
renovate[bot]
f4dbbcc439 chore(deps): update dependency rich to v13.3.3 (#2313)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-03 10:06:04 -08:00
Hayden
4126681f8c New Crowdin updates (#2316)
* New translations en-US.json (Romanian)

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

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)

* New translations en-US.json (Danish)

* New translations en-US.json (Turkish)

* New translations en-US.json (German)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Spanish)

* New translations en-US.json (Catalan)

* New translations en-US.json (French)
2023-04-03 10:05:52 -08:00
rastacalavera
13aa57a3d2 updated iOS shortcut (#2317)
* Update ios.md

updated for version 1.X

* Update ios.md

fixing spaces

* Add files via upload

adding screenshots

* Update ios.md

fixed screen shot path and shrunk images down

* Add files via upload

added smaller screen shots
2023-04-03 10:04:51 -08:00
Michael Genson
73476c10f8 feat: Parser Workflow Improvements (#2249)
* ensure recipe settings are set after parsing

* added insert and delete functionality

* added errors recalculation to use the new index
2023-04-01 17:43:59 -08:00
renovate[bot]
8a0ee351c5 chore(deps): update dependency coverage to v7.2.2 (#2287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-01 17:43:27 -08:00
renovate[bot]
5d61b3a822 chore(deps): update dependency mkdocs-material to v9.1.5 (#2295)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-01 17:43:11 -08:00
rastacalavera
721a1cdeae Update community guide (#2309)
* Update ios.md

updated for version 1.X

* Update ios.md

fixing spaces

* Add files via upload

adding screenshots
2023-04-01 17:42:49 -08:00
renovate[bot]
f7310bc7c3 chore(deps): update dependency mypy to v1.1.1 (#2296)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-01 17:42:31 -08:00
Hayden
b0b06200f8 New Crowdin updates (#2293)
* New translations en-US.json (German)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (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 (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Catalan)

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

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)
2023-04-01 17:42:04 -08:00
renovate[bot]
027fcbdcef fix(deps): update dependency uvicorn to ^0.21.0 (#2233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-24 08:32:24 -08:00
Hayden
ab04d1f595 New Crowdin updates (#2278)
* New translations en-US.json (German)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (German)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (German)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)
2023-03-24 08:31:44 -08:00
renovate[bot]
d56604d19b fix(deps): update dependency sqlalchemy to v2.0.7 (#2286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-24 08:31:21 -08:00
renovate[bot]
8a0552c902 fix(deps): update dependency python-slugify to v8.0.1 (#2282)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-24 08:31:09 -08:00
renovate[bot]
4c0e27b849 fix(deps): update dependency pydantic to v1.10.7 (#2281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-24 08:30:50 -08:00
renovate[bot]
04c7928963 fix(deps): update dependency orjson to v3.8.8 (#2280)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-24 08:30:23 -08:00
Sören
4b426ddf2f Remove all sqlalchemy lazy-loading from app (#2260)
* Remove some implicit lazy-loads from user serialization

* implement full backup restore across different database versions

* rework all custom getter dicts to not leak lazy loads

* remove some occurances of lazy-loading

* remove a lot of lazy loading from recipes

* add more eager loading
remove loading options from repository
remove raiseload for checking

* fix failing test

* do not apply loader options for paging counts

* try using selectinload a bit more instead of joinedload

* linter fixes
2023-03-24 08:27:26 -08:00
renovate[bot]
fae62ecb19 chore(deps): update dependency prettier to v2.8.7 (#2255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-24 08:21:26 -08:00
Sören
82ce4b5e7a fix "no data" error on create meal plan modal (#2263) 2023-03-24 08:21:12 -08:00
renovate[bot]
905b2ad8a9 chore(deps): update dependency @nuxt/types to v2.16.3 (#2270)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-23 10:11:50 -08:00
renovate[bot]
7880735ee4 chore(deps): update dependency vitest to v0.29.7 (#2275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-23 09:42:00 -08:00
renovate[bot]
73e584b59e chore(deps): update dependency rich to v13.3.2 (#2274)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-23 09:41:50 -08:00
Hayden
71ce2be17c New Crowdin updates (#2272)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Dutch)

* New translations en-US.json (Polish)

* New translations en-US.json (German)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

* New translations en-US.json (Hebrew)

* New translations en-US.json (German)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Dutch)

* New translations en-US.json (Hebrew)

* New translations en-US.json (German)
2023-03-23 09:26:40 -08:00
renovate[bot]
667a231f4e chore(deps): update dependency pytest to v7.2.2 (#2271)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-23 09:26:24 -08:00
renovate[bot]
4a08ae6517 chore(deps): update dependency @types/sortablejs to v1.15.1 (#2254)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-23 09:25:04 -08:00
renovate[bot]
6e8628de25 chore(deps): update dependency @nuxtjs/eslint-module to v4 (#2220)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-23 09:24:05 -08:00
Michael Genson
50f50b2b9a Chore: Dev Container Settings (#2253)
* add isort line length to match black

* format settings

* disable formatting for vue files
2023-03-23 09:22:06 -08:00
Hayden
329d2c020d fix: make parser compare lowercase (#2244)
* use case-insensitive matching

* conditionally render delete button
2023-03-23 09:21:22 -08:00
renovate[bot]
f206583150 fix(deps): update dependency python-multipart to ^0.0.6 (#2198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-23 09:20:50 -08:00
Michael Genson
b52848595c feat: added max width for print setting dialog (#2252) 2023-03-21 11:52:18 -08:00
Michael Genson
b460ae9f25 feat: reduced ingredients to one column on mobile (#2251) 2023-03-21 11:51:52 -08:00
Michael Genson
9199ae4901 fix: update_at sort (#2250) 2023-03-21 11:51:19 -08:00
Michael Genson
a025996b94 feat: Expanded safe HTML tags and attributes (#2248)
* expanded safe html tags and attrs

* removed style attr

* add note on sources of safe elements

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-03-21 11:49:41 -08:00
Michael Genson
d2134b27ba fix: Shopping List Mobile Usability (#2247)
* removed handle from edit button

* added dragging delay for touch screens
2023-03-21 11:48:00 -08:00
Michael Genson
f9acba34cc feat: Recipe Data Management UI Improvements (#2246)
* made recipe name clickable

* fixed placeholder user avatar link
2023-03-21 11:47:26 -08:00
Hayden
97d9d10b1f New Crowdin updates (#2243)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)

* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

* New translations en-US.json (Turkish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Polish)

* New translations en-US.json (Italian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)
2023-03-21 11:46:13 -08:00
renovate[bot]
37505d896c chore(deps): update actions/cache action to v3.3.1 (#2230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-21 11:45:42 -08:00
sephrat
9fd1ba6e46 feat(lang): more localization(#2219)
* feat(lang): localize some views

* fix: typo

* fix: Localization broke bug report generation

* feat(lang): localize recipe page instructions
2023-03-21 11:45:27 -08:00
renovate[bot]
6b63c751b1 fix(deps): update dependency fastapi to ^0.95.0 (#2217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-21 11:44:48 -08:00
Hayden
8d79773bf6 fix: ignore nightly and develop in version check (#2242) 2023-03-12 13:41:41 -08:00
Hayden
9650ba9b00 refator: reuse search page component (#2240)
* wip: fix recipe card section

* refactor basic search to share composable

* fix dialog results

* use search for cat/tag/tool pages

* update organizer to support name edits

* fix composable typing
2023-03-12 12:59:28 -08:00
Hayden
b06517fdf4 New Crowdin updates (#2239)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)
2023-03-12 12:57:38 -08:00
Philipp Fischbeck
42d078ef02 fix: frontend register url (#2231) 2023-03-12 12:40:31 -08:00
Sören
ccb0b43cef feat: implement backup restoration from old db schemas (#2213)
* Remove some implicit lazy-loads from user serialization

* implement full backup restore across different database versions
2023-03-12 12:39:51 -08:00
Michael Genson
3118b0e423 feat: Migrate from Copy Me That (#2212)
* implemented copymethat migration

* added migration tree

* added translation support

* genericized example jpgs

* added test data

* fixed test archive

* switched recipe create to service
added test for timeline event creation

* linting

* lxml go brrr
2023-03-12 12:37:24 -08:00
Hayden
3ce8fa9492 New Crowdin updates (#2210)
* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (French)

* New translations en-US.json (French)

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

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Italian)

* New translations en-US.json (French)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Greek)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Finnish)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Czech)

* New translations en-US.json (Spanish)
2023-03-12 12:36:48 -08:00
Carter
7d9be67432 feat: LDAP Improvements and E2E testing (#2199)
* add option to enable starttls for ldap

* add integration test for ldap service

* document new, optional environment variable

* fix: support anonymous bind

* id and mail attributes in LDAP_USER_FILTER should be implied

* remove print statement
2023-03-12 12:36:32 -08:00
Carter
93eb2af087 feature: add password reset token endpoint to the admin panel (#2171)
* add password reset token endpoint to the admin panel

* add None check on token

* add localization message for passowrd reset link button
2023-03-12 12:33:36 -08:00
Hayden
1b26ca0cb3 New Crowdin updates (#2196)
* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Portuguese, Brazilian)
2023-03-04 10:26:02 -09:00
Hayden
11cd277975 ui: rework meal-planner (#2203)
* remove unused TS Ignores

* refactor planner into multiple pages

also includes some minor UI adjustments and some feature work to improve the date selector

* use mobile cards for meal-planner

* remove component
2023-03-02 09:45:06 -09:00
Hayden
5b487464ea New Crowdin updates (#2192)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)

* New translations en-US.json (German)

* New translations en-US.json (Polish)

* New translations en-US.json (German)
2023-02-26 16:04:43 -09:00
Hayden
85bc3a9b15 New Crowdin updates (#2191)
* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (Polish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Polish)

* New translations en-US.json (Turkish)
2023-02-26 12:41:08 -09:00
Hayden
01e3bb04b9 feat: update inline docs on group page to clarify private/public settings (#2190) 2023-02-26 12:39:36 -09:00
Hayden
d3f893dec2 chore: auto-set-version-on-ci (#2188)
* setup __version__ on build

* make build clickable link

* fix else-if
2023-02-26 11:51:04 -09:00
Sören
ac0432d47a fix: cookbook ordering in frontend (#2180)
* fixes cookbook ordering in frontend

* Revert "fixes cookbook ordering in frontend"

This reverts commit 1b5b172911.

* Fix cookbook ordering the proper way
2023-02-26 11:48:32 -09:00
Hayden
8b181ec28a New Crowdin updates (#2187)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)

* New translations en-US.json (Dutch)

* New translations en-US.json (Danish)

* New translations en-US.json (Romanian)

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

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)
2023-02-26 10:34:09 -09:00
Sören
a28133ea5d fix: font loading in css (#2182) 2023-02-26 10:29:11 -09:00
Sören
8824aec066 fix: sequence numbers not updating after backup restore (#2179) 2023-02-26 10:28:09 -09:00
Sören
84d55eb920 fix: #2169 webhooks not firing (#2178)
* fix webhooks not firing due to missing session

* disable webhook test button because it doesnt do anything

* fix background task administration not working at all

* fix error in test
2023-02-26 10:27:22 -09:00
renovate[bot]
da791ec9c5 chore(deps): update dependency vitest to ^0.29.0 (#2177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-26 10:21:34 -09:00
Sören
30d8e0b16d feat: use server-side search for mealplanner (#2174) 2023-02-26 10:21:15 -09:00
Sören
541cdc79aa fix: 2148 infinite scroll on search (#2173)
* refactor recipe card section to use unified query construct

* rework search page so it uses lazy-loading of RecipeCardSection

* remove RecipeQuery again

* prettier reformatting

* remove recipes/all page

* remove max results setting from search

* fix typing issues
2023-02-26 10:20:26 -09:00
Hayden
afbee3a078 New translations en-US.json (French) (#2172) 2023-02-26 10:15:47 -09:00
renovate[bot]
796dcd9995 fix(deps): update dependency python-dotenv to v1 (#2170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-26 10:13:10 -09:00
Michael Genson
8ca0fe42de Feature: More Shopping List Improvements (#2164)
* added color back to labels

* improved mobile view
refactored layout to use grid
allowed text wrapping on item labels
removed label overflow
added completion date on checked items

* sort checked items by last updated

* made checking an item off more responsive

* optimized moving checked items
removed unnecessary updateAll call
removed jitter when shopping list refreshes
2023-02-26 10:12:53 -09:00
Carter
2e6ad5da8e Feature: Add "Authentication Method" to allow existing users to sign in with LDAP (#2143)
* adds authentication method for users

* fix db migration with postgres

* tests for auth method

* update migration ids

* hide auth method on user creation form

* (docs): Added documentation for the new authentication method

* update migration

* add  to auto-form instead of having hidden fields
2023-02-26 10:12:16 -09:00
Hayden
39012adcc1 chore: remove ignore with match statement support (#2175) 2023-02-24 19:13:16 -09:00
Michael Genson
6418a10428 fix: misused update over patch in last_update call (#2168)
* fixed mealplan timeline event task
fixed indentation to only look at one group at a time
changed grumpy update to happy patch

* updated pytest to catch this error

* I don't know how this got past the pre-commit
2023-02-23 13:10:47 -09:00
renovate[bot]
6a1503d1f6 chore(deps): update actions/cache action to v3.2.6 (#2160)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-23 09:30:14 -09:00
renovate[bot]
076410071c chore(deps): update dependency ruff to ^0.0.252 (#2155)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-23 09:30:02 -09:00
Sören
9d35b0923a fix performance issues on /api/foods (#2163)
* fix performance issues on /api/foods

* fix comment

* actually apply query-options
2023-02-21 19:01:27 -09:00
Michael Genson
666085b9ca Fix: Print Preferences Menu Missing (#2162)
* fixed console errors for missing recipe prop

* restored print preferences to action menu
2023-02-21 19:00:22 -09:00
Michael Genson
fd03d468d4 Fix: Allow Last Made to be Updated on Locked Recipes (#2140)
* allow certain props to be updated on locked recipe

* pytest

* added "last_made" to hardcoded datetime fields

* refactored last made to its own route

* codegen/types

* updated pytest
2023-02-21 18:59:22 -09:00
Michael Genson
a6c46a7420 Feature: Shopping List Label Section Improvements (#2090)
* added backend for shopping list label config

* updated codegen

* refactored shopping list ops to service
removed unique contraint
removed label settings from main route/schema
added new route for label settings

* codegen

* made sure label settings output in position order

* implemented submenu for label order drag and drop

* removed redundant label and tweaked formatting

* added view by label to user preferences

* made items draggable within each label section

* moved reorder labels to its own button

* made dialog scrollable

* fixed broken model

* refactored labels to use a service
moved shopping list label logic to service
modified label seeder to use service

* added tests

* fix for first label missing the tag icon

* fixed wrong mapped type

* added statement to create existing relationships

* fix restore test, maybe
2023-02-21 18:58:41 -09:00
Michael Genson
e14851531d fixed last made query value (#2157) 2023-02-20 21:52:16 -09:00
Michael Genson
fb830189d2 remembered how to count over 9 (#2156) 2023-02-20 21:51:58 -09:00
Michael Genson
eaa6ee57a9 Fix: Remove dead search link (#2158)
* removed old references to search page

* changed overflow from scroll to auto
2023-02-20 21:51:24 -09:00
Hayden
df8459a95c feat: use debounced search (optional) (#2150)
* use debounced search (optional)

* replace homepage with search page
2023-02-19 17:13:29 -09:00
Hayden
4f14058251 New Crowdin updates (#2149)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)
2023-02-19 17:07:14 -09:00
Michael Genson
09e58b36af fix: added limited context menu for public users (#2138) 2023-02-19 16:28:57 -09:00
Michael Genson
5562effd66 feat: select ingredients to add to shopping List (#2136)
* added recipe ingredient override to backend

* pytest

* new dialog to filter recipe items added to list
2023-02-19 16:20:32 -09:00
Philipp Fischbeck
89b003589d chore(deps): update to Nuxt 2.16 (Vue 2.7) (#2144) 2023-02-19 16:11:52 -09:00
Hayden
6c0fae51b7 New Crowdin updates (#2147)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)
2023-02-19 15:53:51 -09:00
renovate[bot]
0a54285674 fix(deps): update dependency fastapi to ^0.92.0 (#2128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-19 15:48:33 -09:00
Michael Genson
35124ea875 feat: Shopping List Item Pagination Route (#2145)
* added pagination route for list items

* pytest
2023-02-19 15:48:19 -09:00
Michael Genson
c6d53fe8b1 added validator to trim base url trailing slash (#2142) 2023-02-19 15:46:52 -09:00
Michael Genson
d639bdcfe9 fix: check if the temp dir exists before deleting it (#2141) 2023-02-19 15:46:17 -09:00
Michael Genson
05e2566c35 fix: recipe scraper image cleaning (#2139)
* updated image cleaner
enabled image cleaner
added case for nested image dicts

* refactored image cleaner to return a list of urls
2023-02-19 15:43:52 -09:00
Michael Genson
53fe5921d2 chore: removed debug log (#2137) 2023-02-19 15:41:05 -09:00
Sören
6a5f9d7f6b feat: diacritic-insensitive search (#2132)
* add normalized columns and use them for search

* add migration to fill all normalized columns
2023-02-19 15:40:18 -09:00
Michael Genson
670907b563 feat: Print Preferences (#2131)
* added basic recipe print settings
added print settings dialog
refactored print view to live inside print container

* removed unwanted padding

* changed preferences layout
2023-02-19 15:37:18 -09:00
renovate[bot]
b25cc70963 fix(deps): update dependency isomorphic-dompurify to v1 (#2129)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-19 15:35:42 -09:00
Hayden
f241228b94 New Crowdin updates (#2127)
* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hungarian)

* New translations en-US.json (German)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Dutch)
2023-02-19 15:33:25 -09:00
renovate[bot]
8e43fc6848 chore(deps): update dependency ruff to ^0.0.247 (#2125)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-19 15:32:55 -09:00
Hayden
3a4c37e04f New Crowdin updates (#2122)
* New translations en-US.json (Slovak)

* New translations en-US.json (Dutch)
2023-02-12 15:12:03 -09:00
Hayden
4d8dc7a63c New Crowdin updates (#2120)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (German)

* New translations en-US.json (Greek)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* 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 (French, Canada)

* New translations en-US.json (Dutch)

* New translations en-US.json (Turkish)

* New translations en-US.json (Swedish)

* 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)
2023-02-12 11:16:39 -09:00
Erwan Colin
94abd04d38 docs: postgres volume target is /var/lib/postgresql/data (#2121) 2023-02-12 11:16:11 -09:00
Hayden
71f8c1066a feat: server side search (#2112) (#2117)
* feat: server side search API (#2112)

* refactor repository_recipes filter building

* add food filter to recipe repository page_all

* fix query type annotations

* working search

* add tests and make sure title matches are ordered correctly

* remove instruction matching again

* fix formatting and small issues

* fix another linting error

* make search test no rely on actual words

* fix failing postgres compiled query

* revise incorrectly ordered migration

* automatically extract latest migration version

* test migration orderes

* run type generators

* new search function

* wip: new search page

* sortable field options

* fix virtual scroll issue

* fix search casing bug

* finalize search filters/sorts

* remove old composable

* fix type errors

---------

Co-authored-by: Sören <fleshgolem@gmx.net>
2023-02-11 21:26:10 -09:00
Jon
fc105dcebc Update docker-compose.yml (#2109)
* Update docker-compose.yml

ERROR: The Compose file './docker-compose.yml' is invalid because:
services.mealie.environment.WEB_GUNICORN contains true, which is an invalid type, it should be a string, number, or a null

* Update docker-compose.yml

Also fix SMTP settings, since they aren't in array format as in mealie-frontend.

* Once more, ironing out a few minor issues.

Server status reported this, I think this is the correct value, but I'm happy to revert and/or update the value as needed.

* Revert previous two commits

Per https://github.com/hay-kot/mealie/pull/2109#pullrequestreview-1294610637

* Stray newline

Missed a stray newline that was inadvertently added.
2023-02-11 21:08:39 -09:00
renovate[bot]
7fe74cf997 fix(deps): update dependency aiofiles to v23 (#2108)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-11 10:41:08 -09:00
Sören
7d4a379f0d feat: improve database indexing (#2104)
* add indices to all foreign keys and some fields that are used for ordering and filtering

* add missing migrations

* update migration orders

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-02-11 10:40:53 -09:00
renovate[bot]
f9ba7711ec fix(deps): update dependency fastapi to ^0.91.0 (#2106)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-11 10:21:56 -09:00
Carter
da60e56982 fix: user login and creation with LDAP (#2107)
* Corrected if statement to check if a results was returned by the LDAP search. And decoded the user_attributes from binary data to string

* removed trailing spaces

* Revert asserts in LDAP unit test back

Since an empty tuple is still a result, an user is created and the result should not be false.

* Simplified code

* Extended the LDAP implementation

* fix ldap authentication and user creation

* modified docs to include new LDAP environment variables

* update tests and linting

* add libldap-2.4-2 as runtime dependency for the api

---------

Co-authored-by: Erik Landkroon <eriklandkroon@gmail.com>
2023-02-11 10:16:33 -09:00
renovate[bot]
2a929865e2 chore(deps): update actions/cache action to v3.2.5 (#2110)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-11 10:12:25 -09:00
renovate[bot]
1162021977 fix(deps): update dependency isomorphic-dompurify to ^0.27.0 (#2114)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-11 10:11:59 -09:00
renovate[bot]
92662b3780 chore(deps): update dependency mypy to v1 (#2101)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-11 10:11:25 -09:00
Hayden
2ec88fd010 New Crowdin updates (#2103)
* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (Dutch)
2023-02-11 10:10:40 -09:00
Michael Genson
5f7ac92c96 feat: timeline event for mealplans (#2050)
* added related user to mealplans

* made timeline event message actually optional

* added task to create events for mealplan recipes

* replaced fk constraint ops with bulk ops

* fixed event creation and adjusted query range

* indentation is hard

* added missing recipe id query filter

* added tests
2023-02-11 10:08:53 -09:00
Hayden
9e77a9f367 prs-fleshgolem-2070: feat: sqlalchemy 2.0 (#2096)
* upgrade sqlalchemy to 2.0

* rewrite all db models to sqla 2.0 mapping api

* fix some importing and typing weirdness

* fix types of a lot of nullable columns

* remove get_ref methods

* fix issues found by tests

* rewrite all queries in repository_recipe to 2.0 style

* rewrite all repository queries to 2.0 api

* rewrite all remaining queries to 2.0 api

* remove now-unneeded __allow_unmapped__ flag

* remove and fix some unneeded cases of "# type: ignore"

* fix formatting

* bump black version

* run black

* can this please be the last one. okay. just. okay.

* fix repository errors

* remove return

* drop open API validator

---------

Co-authored-by: Sören Busch <fleshgolem@gmx.net>
2023-02-06 18:43:12 -09:00
Hayden
91cd00976a New Crowdin updates (#2097)
* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)
2023-02-06 18:21:01 -09:00
Sören
a35bc71e53 Fix routes getting listed twice in API documentation (#2079)
* remove duplicate tags from all routes after mounting

* fix missing import
2023-02-05 09:53:19 -09:00
Sören
f4b819899d fix: duplicate network calls on index page (#2085)
* Prevent extra recipe load on index page

* Prevent loading recipes with food for all components but search ones

* add missing change in search page
2023-02-05 09:52:49 -09:00
Hayden
f3a26f864d chore: update linters (#2095)
* update deps

* ruff auto-fixes

* refactor match statements where possible
2023-02-05 09:51:44 -09:00
Hayden
d5efaad2c3 New Crowdin updates (#2075)
* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

* New translations en-US.json (Ukrainian)

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

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

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

* New translations en-US.json (French)

* New translations en-US.json (German)

* New translations en-US.json (Slovak)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (French)

* New translations en-US.json (Russian)

* New translations en-US.json (Russian)

* New translations en-US.json (Russian)

* New translations en-US.json (Russian)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (German)
2023-02-05 09:42:55 -09:00
renovate[bot]
2b8cffd31d chore(deps): update actions/cache action to v3.2.4 (#2080)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-05 09:15:10 -09:00
Michael Genson
4fc4ba934d fix: remove network calls from tests (#2055)
* abstracted scraper get_html method

* applied mock to all scrapers

* fixed incorrect var reference
2023-02-05 09:14:57 -09:00
Michael Genson
20160346d7 fix: moved vuetify options from nuxt cfg to vuetify cfg (#2084) 2023-02-01 18:51:51 -09:00
Hayden
766267961c fix import error (#2082) 2023-01-30 12:01:00 -09:00
Hayden
5dc253799d fix: eslint errors and failing tests (#2078)
* fix eslint errors

* fix failing tests
2023-01-29 13:01:41 -09:00
Hayden
7c766af848 New Crowdin updates (#2074)
* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovak)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Romanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (French)

* New translations en-US.json (Spanish)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Arabic)

* New translations en-US.json (Arabic)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Catalan)

* New translations en-US.json (Catalan)

* New translations en-US.json (Czech)

* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (German)

* New translations en-US.json (Greek)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

* New translations en-US.json (Japanese)

* New translations en-US.json (Korean)

* New translations en-US.json (Korean)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Russian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

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

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Swedish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

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

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

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

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

* New translations en-US.json (Vietnamese)

* New translations en-US.json (Vietnamese)

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

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

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

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

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

* New translations en-US.json (French, Canada)
2023-01-28 16:57:34 -09:00
Hayden
530f7c9d6b logging improvements (#2073)
* Scheduled tasks log to Debug, not Info

* Add LOG_LEVEL config to .env

* Update some other log levels and fix typos

* fix logger initializer

---------

Co-authored-by: Jakob Rubin <647846+Grygon@users.noreply.github.com>
2023-01-28 16:54:44 -09:00
Hayden
9ddb27b2e3 Promote i18n Ally for frontend localization (#2072)
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
2023-01-28 16:41:01 -09:00
sephrat
f8b8680b45 Localize hard-coded texts (#2044)
* feat(lang): localize some views

* feat(lang): an attempt at localizing vuetify (WIP)

* feat(lang): localized some more screens

* feat(lang): localized some more screens again

* feat(lang): hack to localize vuetify

* feat(lang): localize data management pages

* fix linting errors

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-01-28 16:39:51 -09:00
Hayden
754d4c3937 chore: disable PWA in development (#2071) 2023-01-28 16:28:09 -09:00
Philipp
59f43a58d3 Upload recipe step images from mobile devices (#2025)
* Upload recipe step images from mobile devices

This adds a button in the recipe step dropdown, as not all mobile
devices can drag and drop a file into the web page

See #885

* Add progress bar
2023-01-28 16:27:40 -09:00
RealFoxie
0acc260447 Improve the default search by focussing on title and description (#2053)
* ignore field norm + weights for basic search

* better search config for advanced search

* undo (wrong) automatic formatting
2023-01-28 16:07:49 -09:00
renovate[bot]
28d24875a3 fix(deps): update dependency python-slugify to v8 (#2069)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-28 16:06:27 -09:00
Hayden
9dbba60379 New Crowdin updates (#2064)
* New translations en-US.json (Spanish)

* New translations en-US.json (Spanish)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)
2023-01-28 15:51:02 -09:00
Sören
49bd420c10 fix: all recipes performance regressions (#2062)
* Move recipe validations from RecipeSummary to Recipe

* fix RepositoryRecipes loading recipes with ingredients even when load_food is False

* Add eager loading of ingredient units

* fix trying to instantiate PaginationBase with concrete type not being valid for mypy

* fix linting issue
2023-01-28 15:50:26 -09:00
Michael Genson
2340ee5bfb Dev Improvements (#2058)
* added ruff cache to git ignore

* moved venv to project and added interpreter path

* added dummy method to LDAP test

* removed unused setting
2023-01-28 15:49:09 -09:00
renovate[bot]
ed797ef8b2 chore(deps): update dependency ruff to ^0.0.237 (#2035)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-28 15:47:22 -09:00
sephrat
8cac921272 fix: Support special characters in seed data (#2048) 2023-01-28 15:46:48 -09:00
Benjamin Schmid
91f0a92838 doc(single-container): add hint on the exposed ports (#2029) 2023-01-28 15:46:27 -09:00
Michael Genson
617cc1fdfb Refactor Shopping List API (#2021)
* tidied up shopping list item models
redefined recipe refs and updated models
added calculated display attribute to unify shopping list item rendering
added validation to use a food's label if an item's label is null

* fixed schema reference

* refactored shopping list item service
route all operations through one central method to account for edgecases
return item collections for all operations to account for merging
consolidate recipe items before sending them to the shopping list

* made fractions prettier

* replaced redundant display text util

* fixed edgecase for zero quantity items on a recipe

* fix for pre-merging recipe ingredients

* fixed edgecase for merging create_items together

* fixed bug with merged updated items creating dupes

* added test for self-removing recipe ref

* update items are now merged w/ existing items

* refactored service to make it easier to read

* added a lot of tests

* made it so checked items are never merged

* fixed bug with dragging + re-ordering

* fix for postgres cascade issue

* added prevalidator to recipe ref to avoid db error
2023-01-28 15:45:02 -09:00
Sören
3415a9c310 Convert scraper to use async (#1915)
* add httpx depedency for async http requests

* rework scraper strategies to download recipe html asynchronously

* rework recipe_data_service to download recipe images asynchronously

* fix recipe_parser test, so it can use async results

* fix bulk import so that it also works with async scraper

* fix broken recipe_parser tests

* Fix issues found by scanners

* Add additional checks for ingredient and instruction count in test_create_by_url

* Revert changes in test recipe_data
Since we are checking ingredients and instructions in test_create_url now, these would fail with the stored html of recipe data

* Add explicit type annotation in recipe_data_service.largest_content_len

* Fix typo in annotation
2023-01-28 15:43:27 -09:00
Hayden
7275dd2696 New Crowdin updates (#2038)
* 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 (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 (French)

* New translations en-US.json (French)

* New translations en-US.json (Finnish)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Portuguese)

* New translations en-US.json (French)

* New translations en-US.json (French)

* New translations en-US.json (Finnish)

* New translations en-US.json (Finnish)
2023-01-24 13:16:42 -09:00
renovate[bot]
17f7df0747 chore(deps): update dependency vitest to ^0.28.0 (#2019)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-24 13:15:24 -09:00
renovate[bot]
518f9a8228 chore(deps): update actions/cache action to v3.2.3 (#2018)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-24 13:15:03 -09:00
Hayden
c8be922111 New Crowdin updates (#2016)
* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Finnish)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Finnish)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Finnish)

* New translations en-US.json (Finnish)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Finnish)

* New translations en-US.json (Italian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hungarian)
2023-01-14 09:33:14 -09:00
renovate[bot]
f1a31ce33d chore(deps): update dependency ruff to ^0.0.221 (#2022)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-14 09:32:56 -09:00
renovate[bot]
9b05d0dec0 chore(deps): update dependency eslint-plugin-vue to v9.9.0 (#2028)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-14 09:32:43 -09:00
Philipp Fischbeck
1e4230093e fix: fix maintenance page i18n (#2033) 2023-01-14 09:31:47 -09:00
William Brawner
611a105988 fix: devcontainer setup poetry venv setup (#2027)
Signed-off-by: William Brawner <me@wbrawner.com>

Signed-off-by: William Brawner <me@wbrawner.com>
2023-01-14 09:31:00 -09:00
Sören
0cd892059b Fix/issue#2003 - Unable to remove all instructions/ingredients from a recipe (#2008)
* Fix issue where recipes could not have all their ingredients/instructions removed

* Add test for removing all instructions and ingredients from a recipe
2023-01-08 10:46:51 -09:00
Hayden
a72f038247 fix: use markdown for description in cards (#2011) 2023-01-08 09:51:36 -09:00
Hayden
ae59f04b9f fix: allow-iframe-embeds (#2009)
* allow embedding iframes

* fix alignment issue for buttons
2023-01-08 09:50:26 -09:00
Hayden
61ccaded2c New translations en-US.json (Finnish) (#2007) 2023-01-08 09:14:06 -09:00
Michael Genson
856a009dd8 fix: for several Shopping List bugs (#1912)
* prevent list refresh while re-ordering items

* update position of new items to stay at the bottom

* prevent refresh while loading

* copy item while editing so it isn't refreshed

* added loading count to handle overlapping actions

* fixed recipe reference throttling

* prevent merging checked and unchecked items
2023-01-08 08:23:24 -09:00
renovate[bot]
7d94209f3e chore(deps): update dependency ruff to ^0.0.215 (#1996)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-08 08:04:58 -09:00
Hayden
d6e4fd424e New Crowdin updates (#2005)
* New translations en-US.json (Finnish)

* New translations en-US.json (Finnish)

* New translations en-US.json (Finnish)
2023-01-08 08:04:38 -09:00
William Edmisten
7fdab33368 docs: Update references to license as AGPL not MIT (#2006) 2023-01-08 08:04:27 -09:00
renovate[bot]
75a65cec95 fix(deps): update dependency isomorphic-dompurify to ^0.26.0 (#2004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-08 08:04:04 -09:00
renovate[bot]
9774f17f64 fix(deps): update dependency fastapi to ^0.89.0 (#1997)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-07 18:22:08 -09:00
Sören
5e97c32e68 Fix example postgres docker-compose setup in docs (#1998)
* Fix example postgres docker-compose setup in docs
Add a local volume to postgres container so changes get persisted between restarts

* Fix linked volume in postgres doc
2023-01-07 18:21:42 -09:00
Hayden
da5b87950f New translations en-US.json (Portuguese) (#2001) 2023-01-07 18:21:26 -09:00
Hayden
ace1d2f9ee fix: force logout when deleting self - closes #1979 (#2000) 2023-01-07 10:30:45 -09:00
Hayden
fc92c39b7c feat: additional events dispatch (#1999)
* formatting

* add new user type

* add registration event

* publish events on new mealplan

* update UI for supported types + type updates
2023-01-07 10:18:08 -09:00
renovate[bot]
c33b14e3d2 fix(deps): update dependency isomorphic-dompurify to ^0.25.0 (#1990)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-06 21:47:47 -09:00
renovate[bot]
fd71352a1b chore(deps): update dependency mkdocs-material to v9 (#1974)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-06 21:47:29 -09:00
renovate[bot]
e1bc009faf chore(deps): update dependency ruff to ^0.0.212 (#1987)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-06 21:41:10 -09:00
Hayden
55378efe84 Fix/potential-pwa-fix-4 (#1993)
* 🤷‍♂️

* add shortname
2023-01-06 15:26:25 -09:00
Hayden
5dab70fe7e fix: potential-pwa-fix-3 (#1992)
* force redirect when authenticated

* set start_url to empty string per - https://stackoverflow.com/questions/64608408/workbox-is-precaching-urls-without-revision-info-standalone-true-this-is-gene
2023-01-06 10:51:38 -09:00
renovate[bot]
4d98d2174e chore(deps): update actions/setup-node action to v3.6.0 (#1989)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 11:15:10 -09:00
Hayden
d0b6c2f131 fix: potential-pwa-fix-2 - remove start_url (#1986) 2023-01-04 19:08:09 -09:00
Hayden
929c67b1a7 New Crowdin updates (#1984)
* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Bulgarian)
2023-01-04 11:33:40 -09:00
renovate[bot]
3b8684265b chore(deps): update dependency ruff to ^0.0.210 (#1985)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-04 11:33:30 -09:00
Hayden
3e41bba8fb fix: potential pwa fix (#1982)
* set cookie params

* potential fix for pwa issue
2023-01-03 17:36:40 -09:00
renovate[bot]
fcf680fe0b chore(deps): update dependency ruff to ^0.0.209 (#1975)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-03 16:53:06 -09:00
Hayden
24f266c7ab fix: extend-cookie-expire (#1981)
potential fix for #1816
2023-01-03 16:51:33 -09:00
Hayden
3fadb82c75 New Crowdin updates (#1977)
* New translations en-US.json (Bulgarian)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Polish)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (German)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Bulgarian)
2023-01-03 13:11:55 -09:00
renovate[bot]
ab13e8e42e fix(deps): update dependency aniso8601 to v9 (#1971)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-01 15:44:19 -09:00
renovate[bot]
0700220418 fix(deps): update dependency python-slugify to v7 (#1972)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-01 14:07:01 -09:00
renovate[bot]
2fc3873bad chore(deps): update docker/login-action action to v2 (#1963)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-01 13:57:25 -09:00
Hayden
4d5550ad85 chore: mypy, ruff, extract, openapi, and recipes-scraper updates (#1968)
* bump ruff and mypy

* mypy fixes

* bump and fix openapi

* drop debug statement

* bump extruct

* bump recipes-scrapers
2023-01-01 13:47:27 -09:00
renovate[bot]
e329e1cd15 chore(deps): update docker/setup-qemu-action action to v2 (#1966)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-01 12:37:37 -09:00
renovate[bot]
013d67fa15 chore(deps): update docker/setup-buildx-action action to v2 (#1964)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-01 12:28:09 -09:00
renovate[bot]
51cc034ef9 chore(deps): update dependency coverage to v7 (#1959)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-01 11:53:51 -09:00
tomamplius
25ebe2d6fb bugfix : fix user_entry validation control (#1871)
* fix user_entry control

* code optimsation

* poetry syntaxe requirement

* poetry is really strict with python

* resolve linting error

* Update security.py

* fix user_entry = [()]

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-01-01 11:52:49 -09:00
renovate[bot]
a71067e3ec chore(deps): update dependency types-python-slugify to v6 (#1962)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-01 11:29:49 -09:00
renovate[bot]
540ac0a36a chore(deps): update dependency rich to v13 (#1960)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-01 09:21:01 -08:00
Hayden
b50f92420b hotfix: buildpath (#1958)
* fixes

* fix build script name
2022-12-31 09:12:45 -09:00
Hayden
be2399e993 New Crowdin updates (#1956)
* New translations en-US.json (Italian)

* New translations en-US.json (Italian)

* New translations en-US.json (Italian)
2022-12-31 09:02:45 -09:00
renovate[bot]
22024a7738 chore(deps): update actions/cache action to v3 (#1951)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-31 09:02:33 -09:00
renovate[bot]
745a7f481b chore(deps): update actions/checkout action to v3 (#1952)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-31 09:02:19 -09:00
renovate[bot]
cd45f09075 chore(deps): update actions/setup-node action to v3 (#1953)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-31 09:02:07 -09:00
renovate[bot]
05a092215e chore(deps): update actions/setup-python action to v4 (#1955)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-31 09:01:44 -09:00
Hayden
e281f53488 chore: refactor-docker-structure (#1948)
* move dockerfiles to dedicated folder

* consolidate docker related files to docker dir

* update CI references

* experimental omni style container

* update makefile commands

* update references

* fix whitespace

* single container docs

* update build paths

* adds omni style build

* set context
2022-12-31 09:01:15 -09:00
Michael Genson
c4eebaccca Fix for shopping list recipe delete route (#1954)
* fixed broken types

* changed remove recipe route from delete to post
2022-12-30 22:09:22 -09:00
Michael Genson
46cc3898ab feat: Improved Recipe Scaling Support and Shopping List Recipe Refactor (#1847)
* propogate scale changes to print view

* fixed incorrect variable reference

* refactored shopping list recipe routes
cleaned up existing logic
added support for recipe scaling

* updated current revision

* adding to shopping list respects UI recipe scale

* added field annotations

* added tests for recipe scaling

* made column nullable and set to 1 during migration
2022-12-30 13:47:35 -09:00
Hayden
d9c39cc1d0 chore: bump aiofiles, dotenv, fastapi, pydantic, uvicorn (#1944)
* update aiofiles and dotenv

* bump fastapi and pydantic

* update testclient

* bump ruff and uvicorn
2022-12-30 11:44:54 -09:00
renovate[bot]
28cdd485f3 chore(deps): update actions/setup-node action to v2.5.1 (#1935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-30 11:38:43 -09:00
Hayden
e08cccb1f6 fix #1940 (#1942) 2022-12-30 11:27:44 -09:00
renovate[bot]
2e51a581f0 fix(deps): update dependency aiofiles to v0.8.0 (#1926)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-30 11:12:25 -09:00
Hayden
7b5f42f9c0 New Crowdin updates (#1939)
* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Turkish)

* New translations en-US.json (German)

* New translations en-US.json (Italian)

* New translations en-US.json (German)

* New translations en-US.json (Ukrainian)
2022-12-30 11:12:08 -09:00
Hayden
37d0283426 New Crowdin updates (#1934)
* New translations en-US.json (Dutch)

* New translations en-US.json (German)
2022-12-29 16:11:17 -09:00
renovate[bot]
dc6cb5a0c3 fix(deps): update dependency tzdata to v2022 (#1933)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-29 16:11:07 -09:00
renovate[bot]
befb922739 chore(deps): update dependency rich to v12 (#1931)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-29 15:16:43 -09:00
renovate[bot]
36b6f1eecd chore(deps): update dependency ruff to ^0.0.200 (#1924)
* chore(deps): update dependency ruff to ^0.0.200

* resolve ruff errors

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-12-29 14:48:32 -09:00
renovate[bot]
3ed7bc14d8 chore(deps): update actions/cache action to v2.1.7 (#1923)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-29 14:24:21 -09:00
renovate[bot]
9236c1c09a Add renovate.json (#1922)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-29 14:22:02 -09:00
Hayden
3ae72cfda1 chore: fix poetry breaking changes (#1921)
* poetry stop breaking my things

* bump poetry version

* drop dependabot
2022-12-29 14:21:48 -09:00
Hayden
81e0c56484 New Crowdin updates (#1920)
* New translations en-US.json (French)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

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

* New translations en-US.json (Catalan)

* New translations en-US.json (Czech)

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Italian)

* New translations en-US.json (Japanese)

* New translations en-US.json (Korean)

* New translations en-US.json (Norwegian)

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

* New translations en-US.json (Turkish)

* 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 (French, Canada)
2022-12-29 13:27:49 -09:00
dependabot[bot]
741471c670 fix(deps): bump @vueuse/core from 9.6.0 to 9.9.0 in /frontend (#1917)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 9.6.0 to 9.9.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v9.9.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-12-29 13:27:36 -09:00
dependabot[bot]
8732dc546d chore(deps-dev): bump vitest from 0.25.3 to 0.26.2 in /frontend (#1913)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 0.25.3 to 0.26.2.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v0.26.2/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  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-12-29 13:27:28 -09:00
dependabot[bot]
5992bc843a chore(deps-dev): bump eslint from 8.28.0 to 8.30.0 in /frontend (#1906)
Bumps [eslint](https://github.com/eslint/eslint) from 8.28.0 to 8.30.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.28.0...v8.30.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-12-29 13:27:18 -09:00
dependabot[bot]
c712e108bd fix(deps): bump core-js from 3.26.1 to 3.27.0 in /frontend (#1916)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.26.1 to 3.27.0.
- [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/commits/v3.27.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  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-12-29 13:05:49 -09:00
dependabot[bot]
49e05801c1 fix(deps): bump vuetify from 2.6.12 to 2.6.13 in /frontend (#1898)
Bumps [vuetify](https://github.com/vuetifyjs/vuetify/tree/HEAD/packages/vuetify) from 2.6.12 to 2.6.13.
- [Release notes](https://github.com/vuetifyjs/vuetify/releases)
- [Commits](https://github.com/vuetifyjs/vuetify/commits/v2.6.13/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-12-29 13:05:33 -09:00
dependabot[bot]
c9b268041c fix(deps): bump @mdi/js from 7.0.96 to 7.1.96 in /frontend (#1893)
Bumps [@mdi/js](https://github.com/Templarian/MaterialDesign-JS) from 7.0.96 to 7.1.96.
- [Release notes](https://github.com/Templarian/MaterialDesign-JS/releases)
- [Commits](https://github.com/Templarian/MaterialDesign-JS/compare/v7.0.96...v7.1.96)

---
updated-dependencies:
- dependency-name: "@mdi/js"
  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-12-29 13:05:18 -09:00
dependabot[bot]
643f4f5af3 chore(deps-dev): bump prettier from 2.8.0 to 2.8.1 in /frontend (#1889)
Bumps [prettier](https://github.com/prettier/prettier) from 2.8.0 to 2.8.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.8.0...2.8.1)

---
updated-dependencies:
- dependency-name: prettier
  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-12-29 13:04:56 -09:00
dependabot[bot]
d3fb16b64a chore(deps-dev): bump lint-staged from 13.0.4 to 13.1.0 in /frontend (#1882)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 13.0.4 to 13.1.0.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v13.0.4...v13.1.0)

---
updated-dependencies:
- dependency-name: lint-staged
  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-12-29 13:04:44 -09:00
jenscalaerts
83b8ce659e feat: improve automatic ingredient linking (#1836)
* Filtering special characters during automatic linking of ingredients to instructions

Used a unicode group to have a set of all unicode punctuation marks

* allowing for linking of ingredients to instruction at the beginning of a newline in the instruction

* Extracted ingredient matching into a composable and added tests. Ignoring 2 letter words to avoid false matches.

While testing the code 2 letter matches were a large source of false positives.
2022-12-29 13:00:31 -09:00
Michael Genson
19ae89a195 fix: Fixed API Token "Created On" date (#1909)
* exposed created_at on token schema

* fixed token createdAt reference

* made created_at optional for compatibility
2022-12-29 12:49:19 -09:00
Philipp Fischbeck
bd2bad29a8 chore: make admin maintenance pages localizable (#1914) 2022-12-29 12:48:49 -09:00
Hayden
a58701a297 New Crowdin updates (#1907)
* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Polish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (French)

* New translations en-US.json (Turkish)
2022-12-29 12:48:25 -09:00
Hayden
35e73eb530 New Crowdin updates (#1886)
* New translations en-US.json (Danish)

* New translations en-US.json (Danish)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Italian)

* New translations en-US.json (Czech)

* New translations en-US.json (Turkish)

* New translations en-US.json (Norwegian)

* New translations en-US.json (German)

* New translations en-US.json (Romanian)

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

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

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

* 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 (French, Canada)

* New translations en-US.json (German)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (Turkish)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Ukrainian)

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

* New translations en-US.json (Norwegian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Danish)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Norwegian)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Portuguese)
2022-12-18 09:44:21 -09:00
Michael Genson
4e8e2d7510 Feat/recipe timeline event UI (#1831)
* added new icons

* added timeline badge and dialog to action menu

* more icons

* implemented timeline dialog using temporary API

* added route for fetching all timeline events

* formalized API call and added mobile-friendly view

* cleaned tags

* improved last made UI for mobile

* added event context menu with placeholder methods

* adjusted default made this date
set time to 1 minute before midnight
adjusted display to properly interpret UTC

* fixed local date display

* implemented update/delete routes

* fixed formating for long subjects

* added api error handling

* made everything localizable

* fixed weird formatting

* removed unnecessary async

* combined mobile/desktop views w/ conditional attrs
2022-12-11 12:16:55 -09:00
Hayden
f5d401a6a6 New Crowdin updates (#1883)
* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)
2022-12-05 10:06:49 -09:00
Hayden
58e91dc62a New Crowdin updates (#1875)
* 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 (German)
2022-12-04 10:30:12 -09:00
Michael Genson
0e1d778d3a fix: delay dark mode loading to bypass vuetify bug (#1877) 2022-12-04 10:30:02 -09:00
Hayden
6111f9c88c New Crowdin updates (#1872)
* New translations en-US.json (Czech)

* New translations en-US.json (French)

* 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 (French, Canada)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Romanian)

* New translations en-US.json (Spanish)

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

* New translations en-US.json (Afrikaans)

* New translations en-US.json (Arabic)

* New translations en-US.json (Bulgarian)

* New translations en-US.json (Catalan)

* New translations en-US.json (Czech)

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (Greek)

* New translations en-US.json (Finnish)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Swedish)

* New translations en-US.json (Slovenian)

* New translations en-US.json (Turkish)

* New translations en-US.json (Greek)

* New translations en-US.json (Norwegian)

* New translations en-US.json (German)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (Finnish)

* New translations en-US.json (Slovak)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Dutch)

* New translations en-US.json (Polish)

* New translations en-US.json (Portuguese)

* New translations en-US.json (Russian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Italian)

* New translations en-US.json (Swedish)

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

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

* New translations en-US.json (Vietnamese)

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

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

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Turkish)

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

* New translations en-US.json (Japanese)

* New translations en-US.json (Slovenian)

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

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

* New translations en-US.json (Turkish)

* New translations en-US.json (German)

* New translations en-US.json (French)

* New translations en-US.json (Dutch)

* New translations en-US.json (French)

* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (Turkish)

* New translations en-US.json (Ukrainian)

* New translations en-US.json (Ukrainian)

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

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

* New translations en-US.json (Portuguese, Brazilian)
2022-12-01 16:25:27 -09:00
Michael Genson
1b9ff454fb feat: additional recipe sort behavior (#1858)
* changed default sort direction for certain attrs

* added workaround for filtering out null datetimes

* filtered out null-valued results for certain sorts

* removed unecessary parse

* used minyear instead of 1900
2022-11-30 20:59:30 -09:00
Philipp
33dffccaa5 feat: duplicate recipes (#1750)
* feature/frontend: Add duplicate button to recipe

* feature/backend: Add recipe duplication endpoint

* feature/frontend: add duplication API call

* Regenerate API docs

* Fix linter errors

* Fix backend linter error

* Move recipe duplication logic to recipe service

* Add test for recipe duplication

* Improve recipe ingredients copy test

* generate types

* import type

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-11-30 20:57:26 -09:00
dependabot[bot]
e73a72959c chore(deps-dev): bump lint-staged from 13.0.3 to 13.0.4 in /frontend (#1869)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 13.0.3 to 13.0.4.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v13.0.3...v13.0.4)

---
updated-dependencies:
- dependency-name: lint-staged
  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-11-30 20:32:50 -09:00
Jambaldorj Ochirpurev
05dfe38f19 feat: add the fuzzy search on Categories, Tags, and Tools pages (#1867) 2022-11-30 20:31:16 -09:00
Michael Genson
b7ab16a7df fix: shopping list frontend throttling on bulk actions (#1853)
* pause refresh when updating list

* throttle recipe +/- to one concurrent request
2022-11-30 20:29:27 -09:00
Jambaldorj Ochirpurev
1c87a87627 feat: warn use when deleting self (#1848)
* add the alert component on User Management

* refactored the warning text into the language file
2022-11-30 20:26:50 -09:00
Patrick
0801f0a908 ci: add trivy image scanning (#1663)
* add trivy image scanning

* implement as partial workflow

* support both the frontend and backend Dockerfiles for scanning

* fix docker build context location
2022-11-30 20:21:46 -09:00
Hayden
82dc586bac chores: updates-and-linters (#1868)
* switch to ruff

* add ruff

* run ruff --fix

* update ruff

* resolve ruff errors

* drop isort from CI

* fix decorator order
2022-11-30 20:20:28 -09:00
Hayden
fd0e02a5c6 New Crowdin updates (#1866)
* New translations en-US.json (German)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

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

* New translations en-US.json (Ukrainian)

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

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Lithuanian)
2022-11-30 20:18:06 -09:00
dependabot[bot]
5bbc65d610 fix(deps): bump @vueuse/core from 9.5.0 to 9.6.0 in /frontend (#1850)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 9.5.0 to 9.6.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v9.6.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-11-27 11:53:00 -09:00
dependabot[bot]
b93ea9c747 chore(deps-dev): bump eslint-plugin-vue from 9.7.0 to 9.8.0 in /frontend (#1857)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.7.0 to 9.8.0.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.7.0...v9.8.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-11-27 11:52:49 -09:00
dependabot[bot]
d541870c39 chore(deps-dev): bump eslint from 8.27.0 to 8.28.0 in /frontend (#1843)
Bumps [eslint](https://github.com/eslint/eslint) from 8.27.0 to 8.28.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.27.0...v8.28.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-11-27 11:52:40 -09:00
dependabot[bot]
54f9d1df15 chore(deps-dev): bump prettier from 2.7.1 to 2.8.0 in /frontend (#1855)
Bumps [prettier](https://github.com/prettier/prettier) from 2.7.1 to 2.8.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.7.1...2.8.0)

---
updated-dependencies:
- dependency-name: prettier
  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-11-27 11:52:28 -09:00
Hayden
e968cca30a New Crowdin updates (#1856)
* New translations en-US.json (Norwegian)

* New translations en-US.json (German)

* New translations en-US.json (Catalan)

* New translations en-US.json (Romanian)

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

* New translations en-US.json (Slovak)

* New translations en-US.json (Slovenian)

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

* New translations en-US.json (Swedish)

* New translations en-US.json (Czech)

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

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

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

* New translations en-US.json (Russian)

* New translations en-US.json (Japanese)

* New translations en-US.json (Polish)

* New translations en-US.json (Dutch)

* New translations en-US.json (Lithuanian)

* New translations en-US.json (Korean)

* New translations en-US.json (Turkish)

* New translations en-US.json (Italian)

* New translations en-US.json (Hungarian)

* New translations en-US.json (Hebrew)

* New translations en-US.json (Finnish)

* New translations en-US.json (Greek)

* New translations en-US.json (Danish)

* New translations en-US.json (German)

* New translations en-US.json (Norwegian)

* New translations en-US.json (French, Canada)
2022-11-27 11:41:42 -09:00
Michael Genson
ba8f021c14 chore: bump recipe-scraper to latest (#1835) 2022-11-27 11:39:43 -09:00
dependabot[bot]
782b2ee827 chore(deps-dev): bump vitest from 0.25.1 to 0.25.3 in /frontend (#1849)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 0.25.1 to 0.25.3.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v0.25.3/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  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-11-27 11:38:49 -09:00
dependabot[bot]
fcf785d3a3 chore(deps-dev): bump @vue/runtime-dom in /frontend (#1826)
Bumps [@vue/runtime-dom](https://github.com/vuejs/core/tree/HEAD/packages/runtime-dom) from 3.2.41 to 3.2.45.
- [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.45/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-11-27 11:38:31 -09:00
dependabot[bot]
8c04888476 fix(deps): bump isomorphic-dompurify from 0.23.0 to 0.24.0 in /frontend (#1825)
Bumps [isomorphic-dompurify](https://github.com/kkomelin/isomorphic-dompurify) from 0.23.0 to 0.24.0.
- [Release notes](https://github.com/kkomelin/isomorphic-dompurify/releases)
- [Commits](https://github.com/kkomelin/isomorphic-dompurify/compare/v0.23.0...v0.24.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-11-27 11:37:54 -09:00
dependabot[bot]
5fbd0a42fd fix(deps): bump core-js from 3.26.0 to 3.26.1 in /frontend (#1823)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.26.0 to 3.26.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/commits/v3.26.1/packages/core-js)

---
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-11-27 11:37:38 -09:00
Hayden
4b7a1af10b docs: add diagram and notes on sharing recipes (#1865) 2022-11-27 11:29:36 -09:00
Hayden
32c4ce4536 fix: planner date picker local and starting day (#1864)
* fix type definitions

* use day of week setting + fix multiple type errors

* add locale for yyyy-mm-dd format
2022-11-27 11:01:56 -09:00
Hayden
3ad3d92e5f feat: allow password change via container CLI (#1863)
* add password change script

* rename file

* add note in FAQ
2022-11-27 10:35:21 -09:00
Jambaldorj Ochirpurev
ac6554efdd fix: text overflow on RecipeCard (#1830)
* fix the text overflow on RecipeCard

* add a wrapper class for description text for recipes
2022-11-27 10:33:03 -09:00
Hayden
01aac0ca5d docs: account-unlock (#1862)
* fix #1854

* generate spec

* add note about unlock script to FAQ
2022-11-27 10:17:43 -09:00
dependabot[bot]
8ec60668e6 chore(deps-dev): bump @nuxtjs/eslint-config-typescript in /frontend (#1837)
Bumps [@nuxtjs/eslint-config-typescript](https://github.com/nuxt/eslint-config) from 11.0.0 to 12.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/v11.0.0...v12.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-11-19 12:01:23 -09:00
Hayden
46f9ef19c8 New Crowdin updates (#1822)
* New translations en-US.json (Turkish)

* New translations en-US.json (Turkish)
2022-11-14 12:43:08 -09:00
Michael Genson
a2dcdc1adf feat: "I Made This" Dialog (#1801)
* added chef hat

* removed unnecessary log

* modified recipe and recipe timeline event schema
changed timeline event "message" -> "event_message"
added "last made" timestamp to recipe

* added "I made this" dialog to recipe action menu

* added missing field and re-ran code-gen

* moved dialog out of context menu and refactored
removed references in action menu and context menu
refactored dialog to be triggered by a button instead
added route to update recipe last made timestamp
added visual for last made timestamp to recipe header and title

* added sorting by last made

* switched event type to comment

* replaced alter column with pydantic alias

* added tests for event message alias
2022-11-13 14:12:53 -09:00
Hayden
f0e6496001 New Crowdin updates (#1817)
* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)

* New translations en-US.json (Czech)
2022-11-13 14:11:36 -09:00
Felix
cb36258c0f Update example docker compose to beta5 (#1815) 2022-11-12 08:23:01 -09:00
Jambaldorj Ochirpurev
703cfd7da2 fix: auto-opening sidebar #1090 (#1787)
* updated the sidebar; on mobile devices, the sidebar will be closed by default

* updated the AppSideBar

* change variable name

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-11-10 15:41:16 -09:00
dependabot[bot]
2a797a210b fix(deps): bump @nuxtjs/auth-next in /frontend (#1799)
Bumps [@nuxtjs/auth-next](https://github.com/nuxt-community/auth-module) from 5.0.0-1624817847.21691f1 to 5.0.0-1667386184.dfbbb54.
- [Release notes](https://github.com/nuxt-community/auth-module/releases)
- [Changelog](https://github.com/nuxt-community/auth-module/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/auth-module/commits)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 15:34:53 -09:00
dependabot[bot]
00ab10b8a6 fix(deps): bump @vueuse/core from 9.3.1 to 9.5.0 in /frontend (#1809)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 9.3.1 to 9.5.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v9.5.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-11-10 15:26:49 -09:00
dependabot[bot]
d30c633928 chore(deps-dev): bump vitest from 0.24.3 to 0.25.1 in /frontend (#1808)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 0.24.3 to 0.25.1.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v0.25.1/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  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-11-10 15:26:37 -09:00
dependabot[bot]
ca47838920 chore(deps-dev): bump eslint from 8.26.0 to 8.27.0 in /frontend (#1807)
Bumps [eslint](https://github.com/eslint/eslint) from 8.26.0 to 8.27.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.26.0...v8.27.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-11-10 15:26:28 -09:00
dependabot[bot]
f8900531e5 chore(deps-dev): bump eslint-plugin-vue from 9.6.0 to 9.7.0 in /frontend (#1794)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.6.0 to 9.7.0.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.6.0...v9.7.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-11-10 15:26:12 -09:00
1848
f0ed2ad115 feat: allow to overwrite data dir (#1703)
New env variable "DATA_DIR" to overwrite data dir.

Co-authored-by: e <e@e.e>
2022-11-10 15:18:40 -09:00
Michael Genson
e7de0c90a1 feature: live shopping list updates (#1730)
* added polling for changes every 5 seconds

* fixed demi import

* stop polling if the refresh fails too many times

* only poll for changes when the user is active
2022-11-10 15:17:49 -09:00
Hayden
89d0cae51d refactor: rewrite cleaner functions for parsing recipe dicts (#1743)
* rewrite cleaner functions

* unify verbage

* try importing dep during check

* fix syntax

* allow override defaults

* satisfy mypy
2022-11-10 15:16:51 -09:00
Hayden
77316d639b New Crowdin updates (#1795)
* 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-11-10 14:53:24 -09:00
Michael Genson
6ee64535df feat: recipe timeline backend api (#1685)
* added recipe_timeline_events table to db

* added schema and routes for recipe timeline events

* added missing mixin and fixed update schema

* added tests

* adjusted migration revision tree

* updated alembic revision test

* added initial timeline event for new recipes

* added additional tests

* added event bus support

* renamed event_dt to timestamp

* add timeline_events to ignore list

* run code-gen

* use new test routes implementation

* use doc string syntax

* moved event type enum from db to schema

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2022-11-01 00:12:26 -08:00
Hayden
714a080ecb New Crowdin updates (#1783)
* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (Dutch)

* New translations en-US.json (German)

* New translations en-US.json (Polish)
2022-11-01 00:11:40 -08:00
Michael Genson
0dfb3a84cb fix: properly format SMTP from address/name (#1782) 2022-10-27 10:46:53 -08:00
Michael Genson
1e3c0b0859 fix: recipe rating display (#1779)
* remove rating from recipe title

* fixed rating not rendering on landscape mode
2022-10-27 10:45:52 -08:00
dependabot[bot]
49e24140bb fix(deps): bump core-js from 3.25.5 to 3.26.0 in /frontend (#1764)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.25.5 to 3.26.0.
- [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/commits/v3.26.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  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-26 09:41:56 -08:00
dependabot[bot]
0978d8675d chore(deps-dev): bump eslint from 8.25.0 to 8.26.0 in /frontend (#1763)
Bumps [eslint](https://github.com/eslint/eslint) from 8.25.0 to 8.26.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.25.0...v8.26.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-26 09:41:48 -08:00
Hayden
c12ef60038 chore: shorten frontend build time (#1772) 2022-10-25 22:53:30 -08:00
676 changed files with 57102 additions and 22055 deletions

View File

@@ -21,12 +21,12 @@ ENV PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_CREATE=false
POETRY_VIRTUALENVS_IN_PROJECT=true
# prepend poetry and venv to path
ENV PATH="$POETRY_HOME/bin:$PATH"
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python -
RUN curl -sSL https://install.python-poetry.org | python3 -
# RUN poetry config virtualenvs.create false
RUN apt-get update \

10
.flake8
View File

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

View File

@@ -0,0 +1,41 @@
---
title: "YOUR TITLE"
body:
- type: checkboxes
id: checks
attributes:
label: First Check
description: |
Please confirm and check all the following prior to submission. If you do not do this, your
issue may be closed.
options:
- label: I used the GitHub search to find a similar requests and didn't find it.
required: true
- label: Checked the [tasks tagged](https://github.com/hay-kot/mealie/issues?q=is%3Aissue+is%3Aopen+label%3Atask+) issues and verified my feature is not covered
required: true
- type: textarea
id: problem
attributes:
label: Please provide a concise description of the problem that would be addressed by this feature.
validations:
required: true
- type: textarea
id: solution
attributes:
label: Please provide a concise description of the feature that would resolve your issue.
validations:
required: true
- type: textarea
id: considerations
attributes:
label: Please consider and list out some caveats or tradeoffs made in your design decision
validations:
required: true
- type: checkboxes
id: additional-information
attributes:
label: Additional Information
options:
- label: If this is accepted I'm willing to submit a PR to provide this feature
- label: If this is accepted I'm willing to help maintain this feature
- label: I'm willing to sponsor/pay a developer to do this work

View File

@@ -1,12 +1,16 @@
---
name: "[v0.5.x] Bug Report"
description: "submit a bug report for the current release"
name: Bug Report
description: "Submit a bug for the latest version of Mealie"
title: "[BUG] - YOUR TITLE"
labels: ["bug", "triage"]
body:
- type: checkboxes
id: checks
attributes:
label: First Check
description: Please confirm and check all the following options.
description: |
Please confirm and check all the following prior to submission. If you do not do this, your
issue may be closed.
options:
- label: This is not a feature request
required: true
@@ -18,8 +22,8 @@ body:
required: true
- label: I already read the docs and didn't find an answer.
required: true
- label: I have checked for existing issues that have been resolved in v1-beta
required: true
- label: This issue can be replicated on the demo site (https://demo.mealie.io/)
required: false
- type: textarea
id: description
attributes:
@@ -27,6 +31,23 @@ body:
placeholder: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to Reproduce
placeholder: 1) ... 2) ... 3) ...
validations:
required: true
- type: textarea
id: logs
attributes:
label: Please provide relevent logs
validations:
required: true
- type: textarea
id: version
attributes:
label: Mealie Version
- type: dropdown
id: os
attributes:
@@ -42,13 +63,6 @@ body:
validations:
required: true
- type: textarea
id: os-details
id: other
attributes:
label: Deployment Details
description: You can add more details about your operating system here, in particular if you chose "Other". If you are experiencing issues with deployment, please provide your docker-compose or docker commands
- type: input
id: mealie-version
attributes:
label: Mealie Version
validations:
required: true
label: Additional Deployment Details

View File

@@ -1,4 +1,4 @@
contact_links:
- name: Feature Requests
url: https://github.com/hay-kot/mealie/issues/122
about: Please add any Feature Requests here.
url: https://github.com/hay-kot/mealie/discussions/new?category=feature-request
about: Please add any Feature Requests as a Github Discussion using the for in this issue.

View File

@@ -0,0 +1,53 @@
---
name: Error Scraping Recipe
description: "Submit a bug or issues related to scraping recipes"
title: "[SCRAPER] - YOUR TITLE"
labels: ["bug", "triage", "scraper"]
body:
- type: checkboxes
id: checks
attributes:
label: First Check
description: |
Please confirm and check all the following prior to submission. If you do not do this, your
issue may be closed.
options:
- label: I used the GitHub search to find a similar issue and didn't find it.
required: true
- label: |
I have verified that this issue _is not_ related to the underlying library
[hhyrsev/recipe-scrapers](https://github.com/hhursev/recipe-scrapers) by **1)** checking
the [debugger](https://demo.mealie.io/recipe/create/debug) and data is returned, **2)**
verifying that there _are_ errors in the log related to application level code, or
**3)** verified that the site provides recipe data, or is otherwise supported by
[hhyrsev/recipe-scrapers](https://github.com/hhursev/recipe-scrapers)
required: true
- label: This issue can be replicated on the demo site (https://demo.mealie.io/)
required: false
- type: textarea
id: urls
attributes:
label: Please provide 1-5 example URLs that are having errors
validations:
required: true
- type: textarea
id: logs
attributes:
label: |
Please provide your logs for the Mealie container `docker logs <container-id> > mealie.logs`
validations:
required: true
- type: dropdown
id: os
attributes:
label: Deployment
description: What Deployment system are you using?
multiple: true
options:
- Docker (Linux)
- Docker (Windows)
- Docker (Synology)
- Unraid
- Other
validations:
required: true

View File

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

View File

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

View File

@@ -19,6 +19,9 @@ jobs:
uses: ./.github/workflows/partial-frontend.yml
build-release:
permissions:
contents: read
packages: write
name: Build Tagged Release
uses: ./.github/workflows/partial-builder.yml
needs:
@@ -41,24 +44,4 @@ jobs:
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
args: "🚀 New builds of ghcr.io/mealie-recipes/mealie:nightly"

View File

@@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: true
matrix:
# Database ENV Variablse as Specified by Mealie
# Database ENV Variables as Specified by Mealie
Database: [sqlite, postgres]
# Services
@@ -27,13 +27,19 @@ jobs:
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
ports:
- 5432:5432
ldap:
image: rroemhild/test-openldap
ports:
- 10389:10389
- 10636:10636
# Steps
steps:
- name: Check out repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.10"
@@ -54,7 +60,7 @@ jobs:
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
echo "import black;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
@@ -66,12 +72,11 @@ jobs:
poetry add "psycopg2-binary==2.8.6"
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' || steps.cache-validate.outputs.cache-hit-success != 'true'
- name: Formatting (Black & isort)
- name: Formatting (Black)
run: |
poetry run black . --check
poetry run isort . --check-only
- name: Lint (Flake8)
- name: Lint (Ruff)
run: |
make backend-lint
@@ -83,5 +88,17 @@ jobs:
env:
DB_ENGINE: ${{ matrix.Database }}
POSTGRES_SERVER: localhost
LDAP_AUTH_ENABLED: True
LDAP_SERVER_URL: ldap://localhost:10389
LDAP_TLS_INSECURE: true
LDAP_ENABLE_STARTTLS: false
LDAP_BASE_DN: "ou=people,dc=planetexpress,dc=com"
LDAP_QUERY_BIND: "cn=admin,dc=planetexpress,dc=com"
LDAP_QUERY_PASSWORD: "GoodNewsEveryone"
LDAP_USER_FILTER: "(&(|({id_attribute}={input})({mail_attribute}={input}))(|(memberOf=cn=ship_crew,ou=people,dc=planetexpress,dc=com)(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)))"
LDAP_ADMIN_FILTER: "memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com"
LDAP_ID_ATTRIBUTE: uid
LDAP_NAME_ATTRIBUTE: cn
LDAP_MAIL_ATTRIBUTE: mail
run: |
make backend-test

View File

@@ -13,68 +13,34 @@ on:
required: true
jobs:
build-frontend:
publish:
runs-on: ubuntu-latest
name: Build Frontend
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
install: true
uses: docker/setup-buildx-action@v2
- 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"
- name: Override __init__.py
run: |
docker build --push --no-cache \
--tag hkotel/mealie:frontend-${{ inputs.tag }} \
--platform linux/amd64,linux/arm64 .
echo "__version__ = \"${{ inputs.tag }}\"" > ./mealie/__init__.py
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
- name: Build and push Docker image
uses: docker/build-push-action@v4
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 .
file: ./docker/Dockerfile
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/${{ github.repository }}:${{ inputs.tag }}

View File

@@ -12,7 +12,7 @@ jobs:
uses: actions/checkout@master
- name: Setup node env 🏗
uses: actions/setup-node@v2.1.5
uses: actions/setup-node@v3.7.0
with:
node-version: 16
check-latest: true
@@ -22,7 +22,7 @@ jobs:
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Cache node_modules 📦
uses: actions/cache@v2.1.4
uses: actions/cache@v3.3.1
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 }}
@@ -50,7 +50,7 @@ jobs:
uses: actions/checkout@master
- name: Setup node env 🏗
uses: actions/setup-node@v2.1.5
uses: actions/setup-node@v3.7.0
with:
node-version: 16
check-latest: true
@@ -60,7 +60,7 @@ jobs:
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Cache node_modules 📦
uses: actions/cache@v2.1.4
uses: actions/cache@v3.3.1
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 }}

View File

@@ -0,0 +1,31 @@
name: Trivy Container Scanning
on:
workflow_call:
jobs:
build:
name: Build and Scan Container
runs-on: ubuntu-latest
strategy:
fail-fast: true
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build Dockerfile
run: |
docker build -t mealie --file=./docker/Dockerfile .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
ignore-unfixed: true
image-ref: "mealie"
format: "sarif"
output: "trivy-results.sarif"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: "trivy-results.sarif"

View File

@@ -13,3 +13,7 @@ jobs:
frontend-tests:
name: "Frontend and End-to-End Tests"
uses: ./.github/workflows/partial-frontend.yml
container-scanning:
name: "Trivy Container Scanning"
uses: ./.github/workflows/partial-trivy-container-scanning.yml

View File

@@ -32,7 +32,7 @@ jobs:
- build-release
steps:
- name: Checkout main
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master

4
.gitignore vendored
View File

@@ -12,6 +12,7 @@ docs/site/
frontend/dist/
dev/code-generation/generated/*
dev/data/mealie.db-journal
dev/data/backups/*
dev/data/debug/*
dev/data/img/*
@@ -86,6 +87,7 @@ coverage.xml
*.cover
.hypothesis/
.pytest_cache/
.ruff_cache/
test.db
# Translations
@@ -158,3 +160,5 @@ dev/code-generation/generated/test_routes.py
mealie/services/parser_services/crfpp/model.crfmodel
lcov.info
dev/code-generation/openapi.json
.run/

View File

@@ -10,22 +10,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
exclude: ^tests/data/
- repo: https://github.com/asottile/pyupgrade
rev: v3.1.0
hooks:
- id: pyupgrade
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
- id: isort
name: isort (python)
- repo: https://github.com/psf/black
rev: 22.3.0
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: "4.0.1"
hooks:
- id: flake8
additional_dependencies:
- "flake8-print==4.0.0"

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["lokalise.i18n-ally"]
}

42
.vscode/settings.json vendored
View File

@@ -1,5 +1,9 @@
{
"cSpell.enableFiletypes": ["!javascript", "!python", "!yaml"],
"cSpell.enableFiletypes": [
"!javascript",
"!python",
"!yaml"
],
"cSpell.words": [
"chowdown",
"compression",
@@ -14,7 +18,9 @@
"source.organizeImports": false
},
"editor.formatOnSave": true,
"eslint.workingDirectories": ["./frontend"],
"eslint.workingDirectories": [
"./frontend"
],
"files.exclude": {
"**/__pycache__": true,
"**/.DS_Store": true,
@@ -23,30 +29,44 @@
"**/.svn": true,
"**/CVS": true
},
"i18n-ally.enabledFrameworks": ["vue"],
"i18n-ally.enabledFrameworks": [
"vue"
],
"i18n-ally.keystyle": "nested",
"i18n-ally.localesPaths": "frontend/lang/messages",
"i18n-ally.sourceLanguage": "en-US",
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.formatting.provider": "black",
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.linting.flake8Enabled": false,
"python.linting.pylintEnabled": false,
"python.linting.pylintArgs": ["--rcfile=${workspaceFolder}/.pylintrc"],
"python.linting.pylintArgs": [
"--rcfile=${workspaceFolder}/.pylintrc"
],
"python.testing.autoTestDiscoverOnSaveEnabled": false,
"python.testing.pytestArgs": ["tests"],
"python.testing.pytestArgs": [
"tests"
],
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.analysis.typeCheckingMode": "off",
"python.linting.mypyEnabled": true,
"isort.path": ["${workspaceFolder}/.venv/bin/isort"],
"search.mode": "reuseEditor",
"python.testing.unittestArgs": ["-v", "-s", "./tests", "-p", "test_*.py"],
"python.testing.unittestArgs": [
"-v",
"-s",
"./tests",
"-p",
"test_*.py"
],
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"package.json": "package-lock.json, yarn.lock, .eslintrc.js, tsconfig.json, .prettierrc, .editorconfig",
"pyproject.toml": "poetry.lock, alembic.ini, .pylintrc, .flake8",
"pyproject.toml": "poetry.lock, alembic.ini, .pylintrc",
"netlify.toml": "runtime.txt",
"docker-compose.yml": "Dockerfile, .dockerignore, docker-compose.dev.yml, docker-compose.yml",
"README.md": "LICENSE, SECURITY.md"
}
},
"[vue]": {
"editor.formatOnSave": false
},
}

View File

@@ -2,7 +2,7 @@
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]
[![AGPL License][license-shield]][license-url]
[![Docker Pulls][docker-pull]][docker-pull]
[![CodeFactor](https://www.codefactor.io/repository/github/hay-kot/mealie/badge)](https://www.codefactor.io/repository/github/hay-kot/mealie)
@@ -59,7 +59,7 @@ If you are not a coder, you can still contribute financially. Financial contribu
<!-- LICENSE -->
## License
Distributed under the MIT License. See `LICENSE` for more information.
Distributed under the AGPL License. See `LICENSE` for more information.
## Sponsors

View File

@@ -1,7 +1,7 @@
"""Add is_ocr_recipe column to recipes
Revision ID: 089bfa50d0ed
Revises: f30cf048c228
Revises: 188374910655
Create Date: 2022-08-05 17:07:07.389271
"""

View File

@@ -1,7 +1,7 @@
"""add extras to shopping lists, list items, and ingredient foods
Revision ID: 44e8d670719d
Revises: 188374910655
Revises: 089bfa50d0ed
Create Date: 2022-08-29 13:57:40.452245
"""

View File

@@ -0,0 +1,50 @@
"""add recipe_timeline_events table
Revision ID: 2ea7a807915c
Revises: 44e8d670719d
Create Date: 2022-09-27 14:53:14.111054
"""
import sqlalchemy as sa
import mealie.db.migration_types
from alembic import op
# revision identifiers, used by Alembic.
revision = "2ea7a807915c"
down_revision = "44e8d670719d"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"recipe_timeline_events",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("subject", sa.String(), nullable=False),
sa.Column("message", sa.String(), nullable=True),
sa.Column("event_type", sa.String(), nullable=True),
sa.Column("image", sa.String(), nullable=True),
sa.Column("timestamp", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(
["recipe_id"],
["recipes.id"],
),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("recipe_timeline_events")
# ### end Alembic commands ###

View File

@@ -0,0 +1,28 @@
"""added recipe last made timestamp
Revision ID: 1923519381ad
Revises: 2ea7a807915c
Create Date: 2022-11-03 13:10:24.811134
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "1923519381ad"
down_revision = "2ea7a807915c"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("recipes", sa.Column("last_made", sa.DateTime(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("recipes", "last_made")
# ### end Alembic commands ###

View File

@@ -0,0 +1,29 @@
"""add recipe_scale to shopping list item ref
Revision ID: 167eb69066ad
Revises: 1923519381ad
Create Date: 2022-11-22 03:42:45.494567
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "167eb69066ad"
down_revision = "1923519381ad"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("shopping_list_item_recipe_reference", sa.Column("recipe_scale", sa.Float(), nullable=True))
op.execute("UPDATE shopping_list_item_recipe_reference SET recipe_scale = 1")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("shopping_list_item_recipe_reference", "recipe_scale")
# ### end Alembic commands ###

View File

@@ -0,0 +1,43 @@
"""add related user to mealplan
Revision ID: 165d943c64ee
Revises: 167eb69066ad
Create Date: 2023-01-21 16:54:44.368768
"""
import sqlalchemy as sa
import mealie.db.migration_types
from alembic import op
# revision identifiers, used by Alembic.
revision = "165d943c64ee"
down_revision = "167eb69066ad"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("group_meal_plans", schema=None) as batch_op:
batch_op.add_column(sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True))
batch_op.create_index(batch_op.f("ix_group_meal_plans_user_id"), ["user_id"], unique=False)
batch_op.create_foreign_key("fk_user_mealplans", "users", ["user_id"], ["id"])
with op.batch_alter_table("shopping_list_item_recipe_reference", schema=None) as batch_op:
batch_op.alter_column("recipe_scale", existing_type=sa.FLOAT(), nullable=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("shopping_list_item_recipe_reference", schema=None) as batch_op:
batch_op.alter_column("recipe_scale", existing_type=sa.FLOAT(), nullable=True)
with op.batch_alter_table("group_meal_plans", schema=None) as batch_op:
batch_op.drop_constraint("fk_user_mealplans", type_="foreignkey")
batch_op.drop_index(batch_op.f("ix_group_meal_plans_user_id"))
batch_op.drop_column("user_id")
# ### end Alembic commands ###

View File

@@ -0,0 +1,280 @@
"""add missing foreign key and order indices
Revision ID: ff5f73b01a7a
Revises: 165d943c64ee
Create Date: 2023-02-07 20:57:21.066927
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = "ff5f73b01a7a"
down_revision = "165d943c64ee"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_index(op.f("ix_api_extras_created_at"), "api_extras", ["created_at"], unique=False)
op.create_index(op.f("ix_api_extras_recipee_id"), "api_extras", ["recipee_id"], unique=False)
op.create_index(op.f("ix_categories_created_at"), "categories", ["created_at"], unique=False)
op.create_index(op.f("ix_cookbooks_created_at"), "cookbooks", ["created_at"], unique=False)
op.create_index(op.f("ix_cookbooks_group_id"), "cookbooks", ["group_id"], unique=False)
op.create_index(op.f("ix_cookbooks_slug"), "cookbooks", ["slug"], unique=False)
op.create_index(
op.f("ix_cookbooks_to_categories_category_id"), "cookbooks_to_categories", ["category_id"], unique=False
)
op.create_index(
op.f("ix_cookbooks_to_categories_cookbook_id"), "cookbooks_to_categories", ["cookbook_id"], unique=False
)
op.create_index(op.f("ix_cookbooks_to_tags_cookbook_id"), "cookbooks_to_tags", ["cookbook_id"], unique=False)
op.create_index(op.f("ix_cookbooks_to_tags_tag_id"), "cookbooks_to_tags", ["tag_id"], unique=False)
op.create_index(op.f("ix_cookbooks_to_tools_cookbook_id"), "cookbooks_to_tools", ["cookbook_id"], unique=False)
op.create_index(op.f("ix_cookbooks_to_tools_tool_id"), "cookbooks_to_tools", ["tool_id"], unique=False)
op.create_index(op.f("ix_group_data_exports_created_at"), "group_data_exports", ["created_at"], unique=False)
op.create_index(
op.f("ix_group_events_notifier_options_created_at"),
"group_events_notifier_options",
["created_at"],
unique=False,
)
op.create_index(
op.f("ix_group_events_notifiers_created_at"), "group_events_notifiers", ["created_at"], unique=False
)
op.create_index(op.f("ix_group_meal_plan_rules_created_at"), "group_meal_plan_rules", ["created_at"], unique=False)
op.create_index(op.f("ix_group_meal_plan_rules_group_id"), "group_meal_plan_rules", ["group_id"], unique=False)
op.create_index(op.f("ix_group_meal_plans_created_at"), "group_meal_plans", ["created_at"], unique=False)
op.create_index(op.f("ix_group_preferences_created_at"), "group_preferences", ["created_at"], unique=False)
op.create_index(op.f("ix_group_reports_created_at"), "group_reports", ["created_at"], unique=False)
op.create_index(op.f("ix_group_to_categories_category_id"), "group_to_categories", ["category_id"], unique=False)
op.create_index(op.f("ix_group_to_categories_group_id"), "group_to_categories", ["group_id"], unique=False)
op.create_index(op.f("ix_groups_created_at"), "groups", ["created_at"], unique=False)
op.create_index(
op.f("ix_ingredient_food_extras_created_at"), "ingredient_food_extras", ["created_at"], unique=False
)
op.create_index(
op.f("ix_ingredient_food_extras_ingredient_food_id"),
"ingredient_food_extras",
["ingredient_food_id"],
unique=False,
)
op.create_index(op.f("ix_ingredient_foods_created_at"), "ingredient_foods", ["created_at"], unique=False)
op.create_index(op.f("ix_ingredient_foods_group_id"), "ingredient_foods", ["group_id"], unique=False)
op.create_index(op.f("ix_ingredient_foods_label_id"), "ingredient_foods", ["label_id"], unique=False)
op.create_index(op.f("ix_ingredient_units_created_at"), "ingredient_units", ["created_at"], unique=False)
op.create_index(op.f("ix_ingredient_units_group_id"), "ingredient_units", ["group_id"], unique=False)
op.create_index(op.f("ix_invite_tokens_created_at"), "invite_tokens", ["created_at"], unique=False)
op.create_index(op.f("ix_invite_tokens_group_id"), "invite_tokens", ["group_id"], unique=False)
op.create_index(op.f("ix_long_live_tokens_created_at"), "long_live_tokens", ["created_at"], unique=False)
op.create_index(op.f("ix_long_live_tokens_token"), "long_live_tokens", ["token"], unique=False)
op.create_index(op.f("ix_long_live_tokens_user_id"), "long_live_tokens", ["user_id"], unique=False)
op.create_index(op.f("ix_multi_purpose_labels_created_at"), "multi_purpose_labels", ["created_at"], unique=False)
op.create_index(op.f("ix_notes_created_at"), "notes", ["created_at"], unique=False)
op.create_index(op.f("ix_notes_recipe_id"), "notes", ["recipe_id"], unique=False)
op.create_index(op.f("ix_password_reset_tokens_created_at"), "password_reset_tokens", ["created_at"], unique=False)
op.create_index(op.f("ix_password_reset_tokens_user_id"), "password_reset_tokens", ["user_id"], unique=False)
op.create_index(
op.f("ix_plan_rules_to_categories_category_id"), "plan_rules_to_categories", ["category_id"], unique=False
)
op.create_index(
op.f("ix_plan_rules_to_categories_group_plan_rule_id"),
"plan_rules_to_categories",
["group_plan_rule_id"],
unique=False,
)
op.create_index(op.f("ix_plan_rules_to_tags_plan_rule_id"), "plan_rules_to_tags", ["plan_rule_id"], unique=False)
op.create_index(op.f("ix_plan_rules_to_tags_tag_id"), "plan_rules_to_tags", ["tag_id"], unique=False)
op.create_index(op.f("ix_recipe_assets_created_at"), "recipe_assets", ["created_at"], unique=False)
op.create_index(op.f("ix_recipe_assets_recipe_id"), "recipe_assets", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipe_comments_created_at"), "recipe_comments", ["created_at"], unique=False)
op.create_index(op.f("ix_recipe_comments_recipe_id"), "recipe_comments", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipe_comments_user_id"), "recipe_comments", ["user_id"], unique=False)
op.create_index(
op.f("ix_recipe_ingredient_ref_link_created_at"), "recipe_ingredient_ref_link", ["created_at"], unique=False
)
op.create_index(
op.f("ix_recipe_ingredient_ref_link_instruction_id"),
"recipe_ingredient_ref_link",
["instruction_id"],
unique=False,
)
op.create_index(
op.f("ix_recipe_ingredient_ref_link_reference_id"), "recipe_ingredient_ref_link", ["reference_id"], unique=False
)
op.create_index(op.f("ix_recipe_instructions_created_at"), "recipe_instructions", ["created_at"], unique=False)
op.create_index(op.f("ix_recipe_instructions_position"), "recipe_instructions", ["position"], unique=False)
op.create_index(op.f("ix_recipe_instructions_recipe_id"), "recipe_instructions", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipe_nutrition_created_at"), "recipe_nutrition", ["created_at"], unique=False)
op.create_index(op.f("ix_recipe_nutrition_recipe_id"), "recipe_nutrition", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipe_settings_created_at"), "recipe_settings", ["created_at"], unique=False)
op.create_index(op.f("ix_recipe_settings_recipe_id"), "recipe_settings", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipe_share_tokens_created_at"), "recipe_share_tokens", ["created_at"], unique=False)
op.create_index(op.f("ix_recipe_share_tokens_recipe_id"), "recipe_share_tokens", ["recipe_id"], unique=False)
op.create_index(
op.f("ix_recipe_timeline_events_created_at"), "recipe_timeline_events", ["created_at"], unique=False
)
op.create_index(op.f("ix_recipe_timeline_events_recipe_id"), "recipe_timeline_events", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipe_timeline_events_timestamp"), "recipe_timeline_events", ["timestamp"], unique=False)
op.create_index(op.f("ix_recipe_timeline_events_user_id"), "recipe_timeline_events", ["user_id"], unique=False)
op.create_index(op.f("ix_recipes_created_at"), "recipes", ["created_at"], unique=False)
op.create_index(op.f("ix_recipes_name"), "recipes", ["name"], unique=False)
op.create_index(op.f("ix_recipes_ingredients_created_at"), "recipes_ingredients", ["created_at"], unique=False)
op.create_index(op.f("ix_recipes_ingredients_food_id"), "recipes_ingredients", ["food_id"], unique=False)
op.create_index(op.f("ix_recipes_ingredients_position"), "recipes_ingredients", ["position"], unique=False)
op.create_index(op.f("ix_recipes_ingredients_unit_id"), "recipes_ingredients", ["unit_id"], unique=False)
op.create_index(
op.f("ix_recipes_to_categories_category_id"), "recipes_to_categories", ["category_id"], unique=False
)
op.create_index(op.f("ix_recipes_to_categories_recipe_id"), "recipes_to_categories", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipes_to_tags_recipe_id"), "recipes_to_tags", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipes_to_tags_tag_id"), "recipes_to_tags", ["tag_id"], unique=False)
op.create_index(op.f("ix_recipes_to_tools_recipe_id"), "recipes_to_tools", ["recipe_id"], unique=False)
op.create_index(op.f("ix_recipes_to_tools_tool_id"), "recipes_to_tools", ["tool_id"], unique=False)
op.create_index(op.f("ix_report_entries_created_at"), "report_entries", ["created_at"], unique=False)
op.create_index(op.f("ix_report_entries_report_id"), "report_entries", ["report_id"], unique=False)
op.create_index(op.f("ix_server_tasks_created_at"), "server_tasks", ["created_at"], unique=False)
op.create_index(op.f("ix_shopping_list_extras_created_at"), "shopping_list_extras", ["created_at"], unique=False)
op.create_index(
op.f("ix_shopping_list_extras_shopping_list_id"), "shopping_list_extras", ["shopping_list_id"], unique=False
)
op.create_index(
op.f("ix_shopping_list_item_extras_created_at"), "shopping_list_item_extras", ["created_at"], unique=False
)
op.create_index(
op.f("ix_shopping_list_item_extras_shopping_list_item_id"),
"shopping_list_item_extras",
["shopping_list_item_id"],
unique=False,
)
op.create_index(
op.f("ix_shopping_list_item_recipe_reference_created_at"),
"shopping_list_item_recipe_reference",
["created_at"],
unique=False,
)
op.create_index(op.f("ix_shopping_list_items_created_at"), "shopping_list_items", ["created_at"], unique=False)
op.create_index(op.f("ix_shopping_list_items_position"), "shopping_list_items", ["position"], unique=False)
op.create_index(
op.f("ix_shopping_list_items_shopping_list_id"), "shopping_list_items", ["shopping_list_id"], unique=False
)
op.create_index(
op.f("ix_shopping_list_recipe_reference_created_at"),
"shopping_list_recipe_reference",
["created_at"],
unique=False,
)
op.create_index(op.f("ix_shopping_lists_created_at"), "shopping_lists", ["created_at"], unique=False)
op.create_index(op.f("ix_tags_created_at"), "tags", ["created_at"], unique=False)
op.create_index(op.f("ix_tools_created_at"), "tools", ["created_at"], unique=False)
op.create_index(op.f("ix_tools_group_id"), "tools", ["group_id"], unique=False)
op.create_index(op.f("ix_users_created_at"), "users", ["created_at"], unique=False)
op.create_index(op.f("ix_users_to_favorites_recipe_id"), "users_to_favorites", ["recipe_id"], unique=False)
op.create_index(op.f("ix_users_to_favorites_user_id"), "users_to_favorites", ["user_id"], unique=False)
op.create_index(op.f("ix_webhook_urls_created_at"), "webhook_urls", ["created_at"], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_webhook_urls_created_at"), table_name="webhook_urls")
op.drop_index(op.f("ix_users_to_favorites_user_id"), table_name="users_to_favorites")
op.drop_index(op.f("ix_users_to_favorites_recipe_id"), table_name="users_to_favorites")
op.drop_index(op.f("ix_users_created_at"), table_name="users")
op.drop_index(op.f("ix_tools_group_id"), table_name="tools")
op.drop_index(op.f("ix_tools_created_at"), table_name="tools")
op.drop_index(op.f("ix_tags_created_at"), table_name="tags")
op.drop_index(op.f("ix_shopping_lists_created_at"), table_name="shopping_lists")
op.drop_index(op.f("ix_shopping_list_recipe_reference_created_at"), table_name="shopping_list_recipe_reference")
op.drop_index(op.f("ix_shopping_list_items_shopping_list_id"), table_name="shopping_list_items")
op.drop_index(op.f("ix_shopping_list_items_position"), table_name="shopping_list_items")
op.drop_index(op.f("ix_shopping_list_items_created_at"), table_name="shopping_list_items")
op.drop_index(
op.f("ix_shopping_list_item_recipe_reference_created_at"), table_name="shopping_list_item_recipe_reference"
)
op.drop_index(op.f("ix_shopping_list_item_extras_shopping_list_item_id"), table_name="shopping_list_item_extras")
op.drop_index(op.f("ix_shopping_list_item_extras_created_at"), table_name="shopping_list_item_extras")
op.drop_index(op.f("ix_shopping_list_extras_shopping_list_id"), table_name="shopping_list_extras")
op.drop_index(op.f("ix_shopping_list_extras_created_at"), table_name="shopping_list_extras")
op.drop_index(op.f("ix_server_tasks_created_at"), table_name="server_tasks")
op.drop_index(op.f("ix_report_entries_report_id"), table_name="report_entries")
op.drop_index(op.f("ix_report_entries_created_at"), table_name="report_entries")
op.drop_index(op.f("ix_recipes_to_tools_tool_id"), table_name="recipes_to_tools")
op.drop_index(op.f("ix_recipes_to_tools_recipe_id"), table_name="recipes_to_tools")
op.drop_index(op.f("ix_recipes_to_tags_tag_id"), table_name="recipes_to_tags")
op.drop_index(op.f("ix_recipes_to_tags_recipe_id"), table_name="recipes_to_tags")
op.drop_index(op.f("ix_recipes_to_categories_recipe_id"), table_name="recipes_to_categories")
op.drop_index(op.f("ix_recipes_to_categories_category_id"), table_name="recipes_to_categories")
op.drop_index(op.f("ix_recipes_ingredients_unit_id"), table_name="recipes_ingredients")
op.drop_index(op.f("ix_recipes_ingredients_position"), table_name="recipes_ingredients")
op.drop_index(op.f("ix_recipes_ingredients_food_id"), table_name="recipes_ingredients")
op.drop_index(op.f("ix_recipes_ingredients_created_at"), table_name="recipes_ingredients")
op.drop_index(op.f("ix_recipes_name"), table_name="recipes")
op.drop_index(op.f("ix_recipes_created_at"), table_name="recipes")
op.drop_index(op.f("ix_recipe_timeline_events_user_id"), table_name="recipe_timeline_events")
op.drop_index(op.f("ix_recipe_timeline_events_timestamp"), table_name="recipe_timeline_events")
op.drop_index(op.f("ix_recipe_timeline_events_recipe_id"), table_name="recipe_timeline_events")
op.drop_index(op.f("ix_recipe_timeline_events_created_at"), table_name="recipe_timeline_events")
op.drop_index(op.f("ix_recipe_share_tokens_recipe_id"), table_name="recipe_share_tokens")
op.drop_index(op.f("ix_recipe_share_tokens_created_at"), table_name="recipe_share_tokens")
op.drop_index(op.f("ix_recipe_settings_recipe_id"), table_name="recipe_settings")
op.drop_index(op.f("ix_recipe_settings_created_at"), table_name="recipe_settings")
op.drop_index(op.f("ix_recipe_nutrition_recipe_id"), table_name="recipe_nutrition")
op.drop_index(op.f("ix_recipe_nutrition_created_at"), table_name="recipe_nutrition")
op.drop_index(op.f("ix_recipe_instructions_recipe_id"), table_name="recipe_instructions")
op.drop_index(op.f("ix_recipe_instructions_position"), table_name="recipe_instructions")
op.drop_index(op.f("ix_recipe_instructions_created_at"), table_name="recipe_instructions")
op.drop_index(op.f("ix_recipe_ingredient_ref_link_reference_id"), table_name="recipe_ingredient_ref_link")
op.drop_index(op.f("ix_recipe_ingredient_ref_link_instruction_id"), table_name="recipe_ingredient_ref_link")
op.drop_index(op.f("ix_recipe_ingredient_ref_link_created_at"), table_name="recipe_ingredient_ref_link")
op.drop_index(op.f("ix_recipe_comments_user_id"), table_name="recipe_comments")
op.drop_index(op.f("ix_recipe_comments_recipe_id"), table_name="recipe_comments")
op.drop_index(op.f("ix_recipe_comments_created_at"), table_name="recipe_comments")
op.drop_index(op.f("ix_recipe_assets_recipe_id"), table_name="recipe_assets")
op.drop_index(op.f("ix_recipe_assets_created_at"), table_name="recipe_assets")
op.drop_index(op.f("ix_plan_rules_to_tags_tag_id"), table_name="plan_rules_to_tags")
op.drop_index(op.f("ix_plan_rules_to_tags_plan_rule_id"), table_name="plan_rules_to_tags")
op.drop_index(op.f("ix_plan_rules_to_categories_group_plan_rule_id"), table_name="plan_rules_to_categories")
op.drop_index(op.f("ix_plan_rules_to_categories_category_id"), table_name="plan_rules_to_categories")
op.drop_index(op.f("ix_password_reset_tokens_user_id"), table_name="password_reset_tokens")
op.drop_index(op.f("ix_password_reset_tokens_created_at"), table_name="password_reset_tokens")
op.drop_index(op.f("ix_notes_recipe_id"), table_name="notes")
op.drop_index(op.f("ix_notes_created_at"), table_name="notes")
op.drop_index(op.f("ix_multi_purpose_labels_created_at"), table_name="multi_purpose_labels")
op.drop_index(op.f("ix_long_live_tokens_user_id"), table_name="long_live_tokens")
op.drop_index(op.f("ix_long_live_tokens_token"), table_name="long_live_tokens")
op.drop_index(op.f("ix_long_live_tokens_created_at"), table_name="long_live_tokens")
op.drop_index(op.f("ix_invite_tokens_group_id"), table_name="invite_tokens")
op.drop_index(op.f("ix_invite_tokens_created_at"), table_name="invite_tokens")
op.drop_index(op.f("ix_ingredient_units_group_id"), table_name="ingredient_units")
op.drop_index(op.f("ix_ingredient_units_created_at"), table_name="ingredient_units")
op.drop_index(op.f("ix_ingredient_foods_label_id"), table_name="ingredient_foods")
op.drop_index(op.f("ix_ingredient_foods_group_id"), table_name="ingredient_foods")
op.drop_index(op.f("ix_ingredient_foods_created_at"), table_name="ingredient_foods")
op.drop_index(op.f("ix_ingredient_food_extras_ingredient_food_id"), table_name="ingredient_food_extras")
op.drop_index(op.f("ix_ingredient_food_extras_created_at"), table_name="ingredient_food_extras")
op.drop_index(op.f("ix_groups_created_at"), table_name="groups")
op.drop_index(op.f("ix_group_to_categories_group_id"), table_name="group_to_categories")
op.drop_index(op.f("ix_group_to_categories_category_id"), table_name="group_to_categories")
op.drop_index(op.f("ix_group_reports_created_at"), table_name="group_reports")
op.drop_index(op.f("ix_group_preferences_created_at"), table_name="group_preferences")
op.drop_index(op.f("ix_group_meal_plans_created_at"), table_name="group_meal_plans")
op.drop_index(op.f("ix_group_meal_plan_rules_group_id"), table_name="group_meal_plan_rules")
op.drop_index(op.f("ix_group_meal_plan_rules_created_at"), table_name="group_meal_plan_rules")
op.drop_index(op.f("ix_group_events_notifiers_created_at"), table_name="group_events_notifiers")
op.drop_index(op.f("ix_group_events_notifier_options_created_at"), table_name="group_events_notifier_options")
op.drop_index(op.f("ix_group_data_exports_created_at"), table_name="group_data_exports")
op.drop_index(op.f("ix_cookbooks_to_tools_tool_id"), table_name="cookbooks_to_tools")
op.drop_index(op.f("ix_cookbooks_to_tools_cookbook_id"), table_name="cookbooks_to_tools")
op.drop_index(op.f("ix_cookbooks_to_tags_tag_id"), table_name="cookbooks_to_tags")
op.drop_index(op.f("ix_cookbooks_to_tags_cookbook_id"), table_name="cookbooks_to_tags")
op.drop_index(op.f("ix_cookbooks_to_categories_cookbook_id"), table_name="cookbooks_to_categories")
op.drop_index(op.f("ix_cookbooks_to_categories_category_id"), table_name="cookbooks_to_categories")
op.drop_index(op.f("ix_cookbooks_slug"), table_name="cookbooks")
op.drop_index(op.f("ix_cookbooks_group_id"), table_name="cookbooks")
op.drop_index(op.f("ix_cookbooks_created_at"), table_name="cookbooks")
op.drop_index(op.f("ix_categories_created_at"), table_name="categories")
op.drop_index(op.f("ix_api_extras_recipee_id"), table_name="api_extras")
op.drop_index(op.f("ix_api_extras_created_at"), table_name="api_extras")
# ### end Alembic commands ###

View File

@@ -0,0 +1,37 @@
"""add more indices necessary for search
Revision ID: 16160bf731a0
Revises: ff5f73b01a7a
Create Date: 2023-02-10 21:18:32.405130
"""
import sqlalchemy as sa
import mealie.db.migration_types
from alembic import op
# revision identifiers, used by Alembic.
revision = "16160bf731a0"
down_revision = "ff5f73b01a7a"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_index(op.f("ix_recipe_instructions_text"), "recipe_instructions", ["text"], unique=False)
op.create_index(op.f("ix_recipes_description"), "recipes", ["description"], unique=False)
op.create_index(op.f("ix_recipes_ingredients_note"), "recipes_ingredients", ["note"], unique=False)
op.create_index(
op.f("ix_recipes_ingredients_original_text"), "recipes_ingredients", ["original_text"], unique=False
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_recipes_ingredients_original_text"), table_name="recipes_ingredients")
op.drop_index(op.f("ix_recipes_ingredients_note"), table_name="recipes_ingredients")
op.drop_index(op.f("ix_recipes_description"), table_name="recipes")
op.drop_index(op.f("ix_recipe_instructions_text"), table_name="recipe_instructions")
# ### end Alembic commands ###

View File

@@ -0,0 +1,118 @@
"""add normalized search properties
Revision ID: 5ab195a474eb
Revises: 16160bf731a0
Create Date: 2023-02-14 20:45:41.102571
"""
import sqlalchemy as sa
from sqlalchemy import orm, select
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
from text_unidecode import unidecode
import mealie.db.migration_types
from alembic import op
from mealie.db.models._model_utils import GUID
# revision identifiers, used by Alembic.
revision = "5ab195a474eb"
down_revision = "16160bf731a0"
branch_labels = None
depends_on = None
class SqlAlchemyBase(DeclarativeBase):
pass
# Intermediate table definitions
class RecipeModel(SqlAlchemyBase):
__tablename__ = "recipes"
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
name: Mapped[str] = mapped_column(sa.String, nullable=False)
description: Mapped[str | None] = mapped_column(sa.String)
name_normalized: Mapped[str] = mapped_column(sa.String, nullable=False, index=True)
description_normalized: Mapped[str | None] = mapped_column(sa.String, index=True)
class RecipeIngredient(SqlAlchemyBase):
__tablename__ = "recipes_ingredients"
id: Mapped[int] = mapped_column(sa.Integer, primary_key=True)
note: Mapped[str | None] = mapped_column(sa.String)
original_text: Mapped[str | None] = mapped_column(sa.String)
note_normalized: Mapped[str | None] = mapped_column(sa.String, index=True)
original_text_normalized: Mapped[str | None] = mapped_column(sa.String, index=True)
def do_data_migration():
bind = op.get_bind()
session = orm.Session(bind=bind)
recipes = session.execute(select(RecipeModel)).scalars().all()
ingredients = session.execute(select(RecipeIngredient)).scalars().all()
for recipe in recipes:
if recipe.name is not None:
recipe.name_normalized = unidecode(recipe.name).lower().strip()
if recipe.description is not None:
recipe.description_normalized = unidecode(recipe.description).lower().strip()
session.add(recipe)
for ingredient in ingredients:
if ingredient.note is not None:
ingredient.note_normalized = unidecode(ingredient.note).lower().strip()
if ingredient.original_text is not None:
ingredient.original_text_normalized = unidecode(ingredient.original_text).lower().strip()
session.add(ingredient)
session.commit()
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
# Set column to nullable first, since we do not have values here yet
op.add_column("recipes", sa.Column("name_normalized", sa.String(), nullable=True))
op.add_column("recipes", sa.Column("description_normalized", sa.String(), nullable=True))
op.drop_index("ix_recipes_description", table_name="recipes")
op.drop_index("ix_recipes_name", table_name="recipes")
op.create_index(op.f("ix_recipes_description_normalized"), "recipes", ["description_normalized"], unique=False)
op.create_index(op.f("ix_recipes_name_normalized"), "recipes", ["name_normalized"], unique=False)
op.add_column("recipes_ingredients", sa.Column("note_normalized", sa.String(), nullable=True))
op.add_column("recipes_ingredients", sa.Column("original_text_normalized", sa.String(), nullable=True))
op.drop_index("ix_recipes_ingredients_note", table_name="recipes_ingredients")
op.drop_index("ix_recipes_ingredients_original_text", table_name="recipes_ingredients")
op.create_index(
op.f("ix_recipes_ingredients_note_normalized"), "recipes_ingredients", ["note_normalized"], unique=False
)
op.create_index(
op.f("ix_recipes_ingredients_original_text_normalized"),
"recipes_ingredients",
["original_text_normalized"],
unique=False,
)
do_data_migration()
# Make recipes.name_normalized not nullable now that column should be filled for all rows
with op.batch_alter_table("recipes", schema=None) as batch_op:
batch_op.alter_column("name_normalized", nullable=False, existing_type=sa.String())
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_recipes_ingredients_original_text_normalized"), table_name="recipes_ingredients")
op.drop_index(op.f("ix_recipes_ingredients_note_normalized"), table_name="recipes_ingredients")
op.create_index("ix_recipes_ingredients_original_text", "recipes_ingredients", ["original_text"], unique=False)
op.create_index("ix_recipes_ingredients_note", "recipes_ingredients", ["note"], unique=False)
op.drop_column("recipes_ingredients", "original_text_normalized")
op.drop_column("recipes_ingredients", "note_normalized")
op.drop_index(op.f("ix_recipes_name_normalized"), table_name="recipes")
op.drop_index(op.f("ix_recipes_description_normalized"), table_name="recipes")
op.create_index("ix_recipes_name", "recipes", ["name"], unique=False)
op.create_index("ix_recipes_description", "recipes", ["description"], unique=False)
op.drop_column("recipes", "description_normalized")
op.drop_column("recipes", "name_normalized")
# ### end Alembic commands ###

View File

@@ -0,0 +1,69 @@
"""added shopping list label settings
Revision ID: b04a08da2108
Revises: 5ab195a474eb
Create Date: 2023-21-02 22:03:19.837244
"""
from uuid import uuid4
import sqlalchemy as sa
from sqlalchemy.orm.session import Session
import mealie.db.migration_types
from alembic import op
from mealie.db.models.group.shopping_list import ShoppingList
from mealie.db.models.labels import MultiPurposeLabel
# revision identifiers, used by Alembic.
revision = "b04a08da2108"
down_revision = "5ab195a474eb"
branch_labels = None
depends_on = None
def populate_shopping_lists_multi_purpose_labels(shopping_lists_multi_purpose_labels_table: sa.Table, session: Session):
shopping_lists = session.query(ShoppingList).all()
labels = session.query(MultiPurposeLabel).all()
shopping_lists_labels_data: list[dict] = []
for shopping_list in shopping_lists:
for i, label in enumerate(labels):
shopping_lists_labels_data.append(
{"id": uuid4(), "shopping_list_id": shopping_list.id, "label_id": label.id, "position": i}
)
op.bulk_insert(shopping_lists_multi_purpose_labels_table, shopping_lists_labels_data)
session.commit()
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
shopping_lists_multi_purpose_labels_table = op.create_table(
"shopping_lists_multi_purpose_labels",
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.Column("update_at", sa.DateTime(), nullable=True),
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("label_id", mealie.db.migration_types.GUID(), nullable=False),
sa.Column("position", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["label_id"],
["multi_purpose_labels.id"],
),
sa.ForeignKeyConstraint(
["shopping_list_id"],
["shopping_lists.id"],
),
sa.PrimaryKeyConstraint("id", "shopping_list_id", "label_id"),
)
# ### end Alembic commands ###
session = Session(bind=op.get_bind())
populate_shopping_lists_multi_purpose_labels(shopping_lists_multi_purpose_labels_table, session)
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("shopping_lists_multi_purpose_labels")
# ### end Alembic commands ###

View File

@@ -0,0 +1,43 @@
"""add auth_method to user table
Revision ID: 38514b39a824
Revises: b04a08da2108
Create Date: 2023-02-22 21:45:52.900964
"""
import sqlalchemy as sa
import mealie.db.migration_types
from alembic import op
# revision identifiers, used by Alembic.
revision = "38514b39a824"
down_revision = "b04a08da2108"
branch_labels = None
depends_on = None
def is_postgres():
return op.get_context().dialect.name == "postgresql"
authMethod = sa.Enum("MEALIE", "LDAP", name="authmethod")
def upgrade():
if is_postgres():
authMethod.create(op.get_bind())
op.add_column(
"users",
sa.Column("auth_method", authMethod, nullable=False, server_default="MEALIE"),
)
op.execute("UPDATE users SET auth_method = 'LDAP' WHERE password = 'LDAP'")
def downgrade():
with op.batch_alter_table("users", schema=None) as batch_op:
batch_op.drop_column("auth_method")
if is_postgres():
authMethod.drop(op.get_bind())

View File

@@ -0,0 +1,89 @@
"""postgres fuzzy search
Revision ID: b3dbb554ba53
Revises: 38514b39a824
Create Date: 2023-04-13 06:47:04.617131
"""
import sqlalchemy as sa
import mealie.db.migration_types
from alembic import op
import alembic.context as context
from mealie.core.config import get_app_settings
# revision identifiers, used by Alembic.
revision = "b3dbb554ba53"
down_revision = "38514b39a824"
branch_labels = None
depends_on = None
def get_db_type():
return op.get_context().dialect.name
def setup_postgres_trigrams():
op.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;")
op.create_index(
"ix_recipes_name_normalized_gin",
table_name="recipes",
columns=["name_normalized"],
unique=False,
postgresql_using="gin",
postgresql_ops={
"name_normalized": "gin_trgm_ops",
},
)
op.create_index(
"ix_recipes_description_normalized_gin",
table_name="recipes",
columns=["description_normalized"],
unique=False,
postgresql_using="gin",
postgresql_ops={
"description_normalized": "gin_trgm_ops",
},
)
op.create_index(
"ix_recipes_ingredients_note_normalized_gin",
table_name="recipes_ingredients",
columns=["note_normalized"],
unique=False,
postgresql_using="gin",
postgresql_ops={
"note_normalized": "gin_trgm_ops",
},
)
op.create_index(
"ix_recipes_ingredients_original_text_normalized_gin",
table_name="recipes_ingredients",
columns=["original_text_normalized"],
unique=False,
postgresql_using="gin",
postgresql_ops={
"original_text_normalized": "gin_trgm_ops",
},
)
def remove_postgres_trigrams():
op.execute("DROP EXTENSION IF EXISTS pg_trgm;")
op.drop_index("ix_recipes_name_normalized_gin", table_name="recipe")
op.drop_index("ix_recipes_description_normalized_gin", table_name="recipe")
op.drop_index("ix_recipes_ingredients_note_normalized_gin", table_name="recipes_ingredients")
op.drop_index("ix_recipes_ingredients_original_text_normalized_gin", table_name="recipes_ingredients")
def upgrade():
if get_db_type() == "postgresql":
setup_postgres_trigrams()
else:
pass
def downgrade():
if get_db_type() == "postgres":
remove_postgres_trigrams()
else:
pass

View File

@@ -0,0 +1,56 @@
"""added group slug
Revision ID: 04ac51cbe9a4
Revises: b3dbb554ba53
Create Date: 2023-08-06 21:00:34.582905
"""
import sqlalchemy as sa
from slugify import slugify
from sqlalchemy.orm import Session
from alembic import op
from mealie.db.models.group.group import Group
# revision identifiers, used by Alembic.
revision = "04ac51cbe9a4"
down_revision = "b3dbb554ba53"
branch_labels = None
depends_on = None
def populate_group_slugs(session: Session):
groups: list[Group] = session.query(Group).all()
seen_slugs: set[str] = set()
for group in groups:
original_name = group.name
attempts = 0
while True:
slug = slugify(group.name)
if slug not in seen_slugs:
break
attempts += 1
group.name = f"{original_name} ({attempts})"
seen_slugs.add(slug)
group.slug = slug
session.commit()
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("groups", sa.Column("slug", sa.String(), nullable=True))
op.create_index(op.f("ix_groups_slug"), "groups", ["slug"], unique=True)
# ### end Alembic commands ###
session = Session(bind=op.get_bind())
populate_group_slugs(session)
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_groups_slug"), table_name="groups")
op.drop_column("groups", "slug")
# ### end Alembic commands ###

View File

@@ -0,0 +1,28 @@
"""added recipe note to shopping list recipe ref
Revision ID: 1825b5225403
Revises: 04ac51cbe9a4
Create Date: 2023-08-14 19:30:49.103185
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "1825b5225403"
down_revision = "04ac51cbe9a4"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("shopping_list_item_recipe_reference", sa.Column("recipe_note", sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("shopping_list_item_recipe_reference", "recipe_note")
# ### end Alembic commands ###

View File

@@ -0,0 +1,32 @@
"""remove tool name and slug unique contraints
Revision ID: bcfdad6b7355
Revises: 1825b5225403
Create Date: 2023-08-15 16:25:07.058929
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = "bcfdad6b7355"
down_revision = "1825b5225403"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index("ix_tools_name", table_name="tools")
op.create_index(op.f("ix_tools_name"), "tools", ["name"], unique=False)
op.drop_index("ix_tools_slug", table_name="tools")
op.create_index(op.f("ix_tools_slug"), "tools", ["slug"], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_tools_slug"), table_name="tools")
op.create_index("ix_tools_slug", "tools", ["slug"], unique=True)
op.drop_index(op.f("ix_tools_name"), table_name="tools")
op.create_index("ix_tools_name", "tools", ["name"], unique=True)
# ### end Alembic commands ###

View File

@@ -0,0 +1,71 @@
"""added normalized unit and food names
Revision ID: 0341b154f79a
Revises: bcfdad6b7355
Create Date: 2023-09-01 14:55:42.166766
"""
import sqlalchemy as sa
from sqlalchemy import orm, select
from alembic import op
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
# revision identifiers, used by Alembic.
revision = "0341b154f79a"
down_revision = "bcfdad6b7355"
branch_labels = None
depends_on = None
def populate_normalized_fields():
bind = op.get_bind()
session = orm.Session(bind=bind)
units = session.execute(select(IngredientUnitModel)).scalars().all()
for unit in units:
if unit.name is not None:
unit.name_normalized = IngredientUnitModel.normalize(unit.name)
if unit.abbreviation is not None:
unit.abbreviation_normalized = IngredientUnitModel.normalize(unit.abbreviation)
session.add(unit)
foods = session.execute(select(IngredientFoodModel)).scalars().all()
for food in foods:
if food.name is not None:
food.name_normalized = IngredientFoodModel.normalize(food.name)
session.add(food)
session.commit()
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("ingredient_foods", sa.Column("name_normalized", sa.String(), nullable=True))
op.create_index(op.f("ix_ingredient_foods_name_normalized"), "ingredient_foods", ["name_normalized"], unique=False)
op.add_column("ingredient_units", sa.Column("name_normalized", sa.String(), nullable=True))
op.add_column("ingredient_units", sa.Column("abbreviation_normalized", sa.String(), nullable=True))
op.create_index(
op.f("ix_ingredient_units_abbreviation_normalized"),
"ingredient_units",
["abbreviation_normalized"],
unique=False,
)
op.create_index(op.f("ix_ingredient_units_name_normalized"), "ingredient_units", ["name_normalized"], unique=False)
# ### end Alembic commands ###
populate_normalized_fields()
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_ingredient_units_name_normalized"), table_name="ingredient_units")
op.drop_index(op.f("ix_ingredient_units_abbreviation_normalized"), table_name="ingredient_units")
op.drop_column("ingredient_units", "abbreviation_normalized")
op.drop_column("ingredient_units", "name_normalized")
op.drop_index(op.f("ix_ingredient_foods_name_normalized"), table_name="ingredient_foods")
op.drop_column("ingredient_foods", "name_normalized")
# ### end Alembic commands ###

View File

@@ -0,0 +1,172 @@
"""added unique constraints
Revision ID: dded3119c1fe
Revises: 0341b154f79a
Create Date: 2023-10-04 14:29:26.688065
"""
from dataclasses import dataclass
from typing import Any
import sqlalchemy as sa
from sqlalchemy.orm import Session
import mealie.db.migration_types
from alembic import op
from mealie.db.models._model_base import SqlAlchemyBase
# revision identifiers, used by Alembic.
revision = "dded3119c1fe"
down_revision = "0341b154f79a"
branch_labels = None
depends_on = None
@dataclass
class TableMeta:
tablename: str
pk_1: str
pk_2: str
@classmethod
def composite_pk(self, pk_1_val: Any, pk_2_val: Any) -> str:
return "$$".join([pk_1_val, pk_2_val])
def _is_postgres():
return op.get_context().dialect.name == "postgresql"
def _remove_duplicates_from_m2m_table(session: Session, table_meta: TableMeta):
if _is_postgres():
default_pk = "CTID"
else:
default_pk = "ROWID"
# some of these tables are missing defined unique pks, so we have to rely on the database default pk
query = sa.text(
f"""
DELETE FROM {table_meta.tablename}
WHERE EXISTS (
SELECT 1 FROM {table_meta.tablename} t2
WHERE {table_meta.tablename}.{table_meta.pk_1} = t2.{table_meta.pk_1}
AND {table_meta.tablename}.{table_meta.pk_2} = t2.{table_meta.pk_2}
AND {table_meta.tablename}.{default_pk} > t2.{default_pk}
)
"""
)
session.execute(query)
session.commit()
def _remove_duplicates_from_m2m_tables(table_metas: list[TableMeta]):
bind = op.get_bind()
session = Session(bind=bind)
for table_meta in table_metas:
_remove_duplicates_from_m2m_table(session, table_meta)
def upgrade():
_remove_duplicates_from_m2m_tables(
[
# M2M
TableMeta("cookbooks_to_categories", "cookbook_id", "category_id"),
TableMeta("cookbooks_to_tags", "cookbook_id", "tag_id"),
TableMeta("cookbooks_to_tools", "cookbook_id", "tool_id"),
TableMeta("group_to_categories", "group_id", "category_id"),
TableMeta("plan_rules_to_categories", "group_plan_rule_id", "category_id"),
TableMeta("plan_rules_to_tags", "plan_rule_id", "tag_id"),
TableMeta("recipes_to_categories", "recipe_id", "category_id"),
TableMeta("recipes_to_tags", "recipe_id", "tag_id"),
TableMeta("recipes_to_tools", "recipe_id", "tool_id"),
TableMeta("users_to_favorites", "user_id", "recipe_id"),
TableMeta("shopping_lists_multi_purpose_labels", "shopping_list_id", "label_id"),
# Foods/Units/Labels
TableMeta("ingredient_foods", "name", "group_id"),
TableMeta("ingredient_units", "name", "group_id"),
TableMeta("multi_purpose_labels", "name", "group_id"),
]
)
# ### commands auto generated by Alembic - please adjust! ###
# we use batch_alter_table here because otherwise this fails on sqlite
# M2M
with op.batch_alter_table("cookbooks_to_categories") as batch_op:
batch_op.create_unique_constraint("cookbook_id_category_id_key", ["cookbook_id", "category_id"])
with op.batch_alter_table("cookbooks_to_tags") as batch_op:
batch_op.create_unique_constraint("cookbook_id_tag_id_key", ["cookbook_id", "tag_id"])
with op.batch_alter_table("cookbooks_to_tools") as batch_op:
batch_op.create_unique_constraint("cookbook_id_tool_id_key", ["cookbook_id", "tool_id"])
with op.batch_alter_table("group_to_categories") as batch_op:
batch_op.create_unique_constraint("group_id_category_id_key", ["group_id", "category_id"])
with op.batch_alter_table("plan_rules_to_categories") as batch_op:
batch_op.create_unique_constraint("group_plan_rule_id_category_id_key", ["group_plan_rule_id", "category_id"])
with op.batch_alter_table("plan_rules_to_tags") as batch_op:
batch_op.create_unique_constraint("plan_rule_id_tag_id_key", ["plan_rule_id", "tag_id"])
with op.batch_alter_table("recipes_to_categories") as batch_op:
batch_op.create_unique_constraint("recipe_id_category_id_key", ["recipe_id", "category_id"])
with op.batch_alter_table("recipes_to_tags") as batch_op:
batch_op.create_unique_constraint("recipe_id_tag_id_key", ["recipe_id", "tag_id"])
with op.batch_alter_table("recipes_to_tools") as batch_op:
batch_op.create_unique_constraint("recipe_id_tool_id_key", ["recipe_id", "tool_id"])
with op.batch_alter_table("users_to_favorites") as batch_op:
batch_op.create_unique_constraint("user_id_recipe_id_key", ["user_id", "recipe_id"])
with op.batch_alter_table("shopping_lists_multi_purpose_labels") as batch_op:
batch_op.create_unique_constraint("shopping_list_id_label_id_key", ["shopping_list_id", "label_id"])
# Foods/Units/Labels
with op.batch_alter_table("ingredient_foods") as batch_op:
batch_op.create_unique_constraint("ingredient_foods_name_group_id_key", ["name", "group_id"])
with op.batch_alter_table("ingredient_units") as batch_op:
batch_op.create_unique_constraint("ingredient_units_name_group_id_key", ["name", "group_id"])
with op.batch_alter_table("multi_purpose_labels") as batch_op:
batch_op.create_unique_constraint("multi_purpose_labels_name_group_id_key", ["name", "group_id"])
op.create_index(
op.f("ix_shopping_lists_multi_purpose_labels_created_at"),
"shopping_lists_multi_purpose_labels",
["created_at"],
unique=False,
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
# M2M
op.drop_constraint("user_id_recipe_id_key", "users_to_favorites", type_="unique")
op.drop_index(
op.f("ix_shopping_lists_multi_purpose_labels_created_at"), table_name="shopping_lists_multi_purpose_labels"
)
op.drop_constraint("recipe_id_tool_id_key", "recipes_to_tools", type_="unique")
op.drop_constraint("recipe_id_tag_id_key", "recipes_to_tags", type_="unique")
op.drop_constraint("recipe_id_category_id_key", "recipes_to_categories", type_="unique")
op.drop_constraint("plan_rule_id_tag_id_key", "plan_rules_to_tags", type_="unique")
op.drop_constraint("group_plan_rule_id_category_id_key", "plan_rules_to_categories", type_="unique")
op.drop_constraint("group_id_category_id_key", "group_to_categories", type_="unique")
op.drop_constraint("cookbook_id_tool_id_key", "cookbooks_to_tools", type_="unique")
op.drop_constraint("cookbook_id_tag_id_key", "cookbooks_to_tags", type_="unique")
op.drop_constraint("cookbook_id_category_id_key", "cookbooks_to_categories", type_="unique")
op.drop_constraint("shopping_list_id_label_id_key", "shopping_lists_multi_purpose_labels", type_="unique")
# Foods/Units/Labels
op.drop_constraint("multi_purpose_labels_name_group_id_key", "multi_purpose_labels", type_="unique")
op.drop_constraint("ingredient_units_name_group_id_key", "ingredient_units", type_="unique")
op.drop_constraint("ingredient_foods_name_group_id_key", "ingredient_foods", type_="unique")
# ### end Alembic commands ###

View File

@@ -25,7 +25,6 @@ def get_path_objects(app: FastAPI):
for key, value in app.openapi().items():
if key == "paths":
for key, value in value.items():
paths.append(
PathObject(
route_object=RouteObject(key),
@@ -50,7 +49,6 @@ def read_template(file: Path):
def generate_python_templates(static_paths: list[PathObject], function_paths: list[PathObject]):
template = Template(read_template(CodeTemplates.pytest_routes))
content = template.render(
paths={

View File

@@ -79,14 +79,12 @@ def find_modules(root: pathlib.Path) -> list[Modules]:
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:

View File

@@ -231,8 +231,7 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic
}
def login(username="changeme@email.com", password="MyPassword"):
def login(username="changeme@example.com", password="MyPassword"):
payload = {"username": username, "password": password}
r = requests.post("http://localhost:9000/api/auth/token", payload)

View File

@@ -1,48 +0,0 @@
# WARNING: currently not functional, see #756, #1072
# Use root/example as user/password credentials
version: "3.4"
services:
# Vue Frontend
mealie-frontend:
container_name: mealie-frontend
image: mealie-frontend:dev
build:
context: ./frontend
dockerfile: Dockerfile.frontend
restart: always
ports:
- 9920:8080
environment:
- GLOBAL_MIDDLEWARE=null
- BASE_URL=""
- ALLOW_SIGNUP=true
volumes:
- ./frontend/:/app
- /app/node_modules
# Fast API
mealie-api:
container_name: mealie-api
image: mealie-api:dev
build:
context: ./
target: development
dockerfile: Dockerfile
restart: always
ports:
- 9921:9000
environment:
TZ: America/Anchorage # Specify Correct Timezone for Date/Time to line up correctly.
volumes:
- ./dev/data:/app/dev/data
- ./mealie:/app/mealie
# Mkdocs
mealie-docs:
container_name: mealie-docs
image: squidfunk/mkdocs-material
restart: always
ports:
- 9922:8000
volumes:
- ./docs:/docs

View File

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

View File

@@ -1,5 +1,21 @@
FROM node:16 as builder
WORKDIR /app
COPY ./frontend .
RUN yarn install \
--prefer-offline \
--frozen-lockfile \
--non-interactive \
--production=false \
# https://github.com/docker/build-push-action/issues/471
--network-timeout 1000000
RUN yarn generate
###############################################
# Base Image
# Base Image - Python
###############################################
FROM python:3.10-slim as python-base
@@ -38,10 +54,11 @@ RUN apt-get update \
# LDAP Dependencies
libsasl2-dev libldap2-dev libssl-dev \
gnupg gnupg2 gnupg1 \
&& rm -rf /var/lib/apt/lists/* \
&& pip install -U --no-cache-dir pip
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
ENV POETRY_VERSION=1.2.1
ENV POETRY_VERSION=1.3.1
RUN curl -sSL https://install.python-poetry.org | python3 -
# copy project requirement files here to ensure they will be cached.
@@ -49,34 +66,7 @@ WORKDIR $PYSETUP_PATH
COPY ./poetry.lock ./pyproject.toml ./
# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally
RUN poetry install -E pgsql --no-dev
###############################################
# Development Image
###############################################
FROM python-base as development
ENV PRODUCTION=false
ENV TESTING=false
# copying poetry and venv into image
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
# copy backend
COPY ./mealie $MEALIE_HOME/mealie
COPY ./poetry.lock ./pyproject.toml $MEALIE_HOME/
# Alembic
COPY ./alembic $MEALIE_HOME/alembic
COPY ./alembic.ini $MEALIE_HOME/
# venv already has runtime deps installed we get a quicker install
WORKDIR $MEALIE_HOME
RUN . $VENV_PATH/bin/activate && poetry install
WORKDIR /
RUN chmod +x $MEALIE_HOME/mealie/run.sh
ENTRYPOINT $MEALIE_HOME/mealie/run.sh "reload"
RUN poetry install -E pgsql --only main
###############################################
# CRFPP Image
@@ -98,8 +88,9 @@ ENV GIT_COMMIT_HASH=$COMMIT
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
gosu \
iproute2 \
tesseract-ocr-all \
&& apt-get autoremove \
libldap-common \
&& rm -rf /var/lib/apt/lists/*
# copying poetry and venv into image
@@ -122,7 +113,7 @@ COPY ./alembic.ini $MEALIE_HOME/
# venv already has runtime deps installed we get a quicker install
WORKDIR $MEALIE_HOME
RUN . $VENV_PATH/bin/activate && poetry install -E pgsql --no-dev
RUN . $VENV_PATH/bin/activate && poetry install -E pgsql --only main
WORKDIR /
# Grab CRF++ Model Release
@@ -135,5 +126,17 @@ EXPOSE ${APP_PORT}
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
# ----------------------------------
# Copy Frontend
# copying caddy into image
ENV STATIC_FILES=/spa/static
COPY --from=builder /app/dist ${STATIC_FILES}
ENV HOST 0.0.0.0
EXPOSE ${APP_PORT}
COPY ./docker/entry.sh $MEALIE_HOME/run.sh
RUN chmod +x $MEALIE_HOME/run.sh
ENTRYPOINT $MEALIE_HOME/run.sh

45
docker/docker-compose.yml Normal file
View File

@@ -0,0 +1,45 @@
version: "3.4"
services:
mealie:
container_name: mealie
image: mealie:dev
build:
context: ../
target: production
dockerfile: ./docker/Dockerfile
restart: always
volumes:
- mealie-data:/app/data/
ports:
- 9091:9000
environment:
ALLOW_SIGNUP: "false"
DB_ENGINE: sqlite # Optional: 'sqlite', 'postgres'
# =====================================
# Postgres Config
POSTGRES_USER: mealie
POSTGRES_PASSWORD: mealie
POSTGRES_SERVER: postgres
POSTGRES_PORT: 5432
POSTGRES_DB: mealie
# =====================================
# Web Concurrency
WEB_GUNICORN: "false"
WORKERS_PER_CORE: 0.5
MAX_WORKERS: 1
WEB_CONCURRENCY: 1
# =====================================
# Email Configuration
# SMTP_HOST=
# SMTP_PORT=587
# SMTP_FROM_NAME=Mealie
# SMTP_AUTH_STRATEGY=TLS # Options: 'TLS', 'SSL', 'NONE'
# SMTP_FROM_EMAIL=
# SMTP_USER=
# SMTP_PASSWORD=
volumes:
mealie-data:
driver: local

41
mealie/run.sh → docker/entry.sh Executable file → Normal file
View File

@@ -1,9 +1,9 @@
# Start Backend API
#!/bin/bash
set -e
# Get Reload Arg `run.sh reload` for dev server
ARG1=${1:-production}
# Strict Mode
# set -e
# IFS=$'\n\t'
# Get PUID/PGID
PUID=${PUID:-911}
@@ -22,7 +22,7 @@ change_user() {
echo "Switching to dedicated user"
exec gosu $PUID "$BASH_SOURCE" "$@"
elif [ "$(id -u)" = $PUID ]; then
elif [ "$(id -u)" = $PUID ]; then
echo "
User uid: $PUID
User gid: $PGID
@@ -41,28 +41,15 @@ init() {
poetry run python /app/mealie/db/init_db.py
}
if [ "$ARG1" == "reload" ]; then
echo "Hot Reload!"
# change_user
init
GUNICORN_PORT=${API_PORT:-9000}
init
# Start API
python /app/mealie/app.py
# Start API
hostip=`/sbin/ip route|awk '/default/ { print $3 }'`
if [ "$WEB_GUNICORN" = 'true' ]; then
echo "Starting Gunicorn"
gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT --forwarded-allow-ips=$hostip -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload
else
echo "Production"
change_user
init
GUNICORN_PORT=${API_PORT:-9000}
# Start API
if [ $WEB_GUNICORN == 'true' ]; then
echo "Starting Gunicorn"
gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload
else
uvicorn mealie.app:app --host 0.0.0.0 --port $GUNICORN_PORT
fi
uvicorn mealie.app:app --host 0.0.0.0 --forwarded-allow-ips=$hostip --port $GUNICORN_PORT
fi

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

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

View File

@@ -6,18 +6,19 @@
We use github to host code, to track issues and feature requests, as well as accept pull requests.
## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests
Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests:
Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests:
1. Fork the repo and create your branch from `dev`.
1. Fork the repo and create your branch from `mealie-next`.
2. Checkout the Discord, the PRs page, or the Projects page to get an idea of what's already being worked on.
3. If you're interested on working on major changes please get in touch on discord and coordinate with other developers. No sense in doubling up on work if someones already on it.
3. If you're interested on working on major changes please get in touch on discord and coordinate with other developers. No sense in doubling up on work if someones already on it.
4. Once you've got an idea of what changes you want to make, create a draft PR as soon as you can to let us know what you're working on and how we can help!
5. If you've changed APIs, update the documentation.
6. Issue that pull request!
7. If you make changes to the dev branch reflect those changes in the active changelog to keep track of changes. Don't forget to add your name/handle/identifier!
6. Run tests, including `make backend-all`. Note that the tests do not clean up after themselves and leave things in the database. So be sure to also run `make clean-data` and/or `make backend-clean` inbetween major testing rounds to be sure that you aren't testing on old data.
6. Issue that pull request! First make a draft PR, make sure that the automated github tests all pass, then mark as ready for review.
7. Be sure to add release notes to the pull request.
## Any contributions you make will be under the MIT Software License
In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.
## Any contributions you make will be under the AGPL Software License
In short, when you submit code changes, your submissions are understood to be under the same [AGPL License](https://choosealicense.com/licenses/agpl-3.0/) that covers the project. Feel free to contact the maintainers if that's a concern.
## Report bugs using Github's [issues](https://github.com/hay-kot/mealie/issues)
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/hay-kot/mealie/issues/new); it's that easy!
@@ -37,7 +38,7 @@ People *love* thorough bug reports. I'm not even kidding.
## License
By contributing, you agree that your contributions will be licensed under its MIT License.
By contributing, you agree that your contributions will be licensed under its AGPL License.
## References
This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)

View File

@@ -13,9 +13,13 @@ Prerequisites
- Docker
- Visual Studio Code
### Linux and MacOS
First ensure that docker is running. Then when you clone the repo and open with VS Code you should see a popup asking you to reopen the project inside a development container. Click yes and it will build the development container and run the setup required to run both the backend API and the frontend webserver. This also pre-configures pre-commit hooks to ensure that the code is up to date before committing.
Checkout the makefile for all of the available commands.
### Windows
Make sure the VSCode Dev Containers extension is installed, then select "Dev Containers: Clone Repository in Container Volume..." in the command pallete (F1). Select your forked repo and choose the `mealie-next` branch, which contains the latest changes. This mounts your repository directly in WSL2, which [greatly improves the performance of the container](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-a-git-repository-or-github-pr-in-an-isolated-container-volume), and enables hot-reloading for the frontend. Running the container on a mounted volume may not work correctly on Windows due to WSL permission mapping issues.
[Checkout the makefile reference](#make-file-reference) for all of the available commands.
!!! tip
For slow terminal checkout the solution in this [GitHub Issue](https://github.com/microsoft/vscode/issues/133215)
@@ -62,9 +66,13 @@ Once the prerequisites are installed you can cd into the project base directory
Before you start the server you MUST copy the `template.env` and `frontend/template.env` files to their respective locations with the name `.env` and `frontend/.env` respectively. The application will-not run without these files.
### Starting The Server
## Postgres
- Whether using a container or manual install, you need to set up your own postgres dev server. The database, username, password, etc should match the `POSTGRES_*` options located in the `.env` file.
- Install psycog2 with `poetry install -E pgsql` (in the main `mealie` directory, *not* `frontend`)
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.
## Starting The Server
Now you're ready to start the servers. You'll need two shells open, One for the server and one for the frontend.
=== "Linux / macOS"
@@ -114,3 +122,24 @@ docker-dev 🐳 Build and Start Docker Development Stack (currently no
docker-prod 🐳 Build and Start Docker Production Stack
```
## Internationalization
### Frontend
We use vue-i18n package for internationalization. Translations are stored in json format located in [frontend/lang/messages](https://github.com/hay-kot/mealie/tree/mealie-next/frontend/lang/messages).
### Backend
Translations are stored in json format located in [mealie/lang/messages](https://github.com/hay-kot/mealie/tree/mealie-next/mealie/lang/messages).
### Quick frontend localization with VS Code
[i18n Ally for VScode](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) is helpful for generating new strings to translate using Code Actions. It also has a nice feature, which shows translations in-place when editing code.
A few settings must be tweaked to make the most of its features. Some settings are stored on project level, but most of them have to be set manually in your workspace or user settings.\
We've found that the following settings work best:
```
"i18n-ally.enabledFrameworks": ["vue"],
"i18n-ally.extract.autoDetect": true,
"i18n-ally.dirStructure": "auto",
"i18n-ally.extract.targetPickingStrategy": "global-previous",
"i18n-ally.displayLanguage": "en-US",
"i18n-ally.keystyle": "nested",
"i18n-ally.sourceLanguage": "en-US",
```

View File

@@ -1,6 +1,6 @@
# Contributing with Translations
Translations can be a great way **for non-coders** to contribute to project.
We use **[Crowdin](https://crowdin.com/project/mealie)** to allow several contributors to work on translating Mealie.
We use **[Crowdin](https://crowdin.com/project/mealie)** to allow several contributors to work on translating Mealie.
You can simply help by voting for your preferred translations, or even by completely translating Mealie into a new language.
Translations are regularly pulled from Crowdin and included in each new release.
@@ -8,14 +8,9 @@ Translations are regularly pulled from Crowdin and included in each new release.
Please use Crowdin as much as possible if you have any question/issue regarding a particular string. You can take a look at [Crowdin Knowledge base](https://support.crowdin.com/for-volunteer-translators/) if you want to know more about how to use this tool.
## My language is missing in Mealie
Once your language is translated on Crowdin, we need to manually add it in Mealie. If you believe your language is ready for use, please create an issue on GitHub.
Once your language is translated on Crowdin, we need to manually add it in Mealie. If you believe your language is ready for use, please create an issue on GitHub.
## I can't find a particular text in Crowdin
There can be several reasons:
- The text you're looking for is outdated: someone has already changed it or it will be removed/changed in the next release.
- It is possible some texts are not translatable (yet) for technical reasons. If you spot one, please reach out to us on [Discord](https://discord.gg/QuStdQGSGK) or raise an issue on GitHub.
## Technical information
We use vue-i18n package for internationalization. Translations are stored in json format located in [frontend/src/locales/messages](https://github.com/hay-kot/mealie/tree/master/frontend/src/locales/messages).
[i18n Ally for VScode](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) is helpful for generating new strings to translate. It also has a nice feature, which shows translations in-place when editing code.

View File

@@ -33,7 +33,7 @@ function import_from_file () {
}
input="list"
mail="changeme@email.com"
mail="changeme@example.com"
password="MyPassword"
mealie_url=http://localhost:9000
@@ -85,7 +85,7 @@ def import_from_file(input_file, token, mealie_url):
print(response.text)
input_file="list"
mail="changeme@email.com"
mail="changeme@example.com"
password="MyPassword"
mealie_url="http://localhost:9000"

View File

@@ -1,25 +1,77 @@
!!! info
This guide was submitted by a community member. Find something wrong? Submit a PR to get it fixed!
This guide was submitted by a community member. Find something wrong? Submit a PR to get it fixed!
In a lot of ways, Home Assistant is why this project exists! Since Mealie has a robust API it makes it a great fit for interacting with Home Assistant and pulling information into your dashboard.
### Get Todays Meal in Lovelace
Starting in v0.4.1 you are now able to use the uri `/api/meal-plans/today/image?group_name=Home` to directly access the image to todays meal. This makes it incredibly easy to include the image into your Home Assistant Dashboard using the picture entity.
### Display Today's Meal in Lovelace
Here's an example where `sensor.mealie_todays_meal` is pulling in the meal-plan name and I'm using the url to get the image.
You can use the Mealie API to get access to meal plans in Home Assistant like in the image below.
![api-extras-gif](../../assets/img/home-assistant-card.png)
Steps:
#### 1. Get your API Token
Create an API token from Mealie's User Settings page (https://hay-kot.github.io/mealie/documentation/users-groups/user-settings/#api-key-generation)
#### 2. Create Home Assistant Sensors
Create REST sensors in home assistant to get the details of today's meal.
We will create sensors to get the name and ID of the first meal in today's meal plan (note that this may not be what is wanted if there is more than one meal planned for the day). We need the ID as well as the name to be able to retreive the image for the meal.
Make sure the url and port (`http://mealie:9000` ) matches your installation's address and _API_ port.
```yaml
- platform: rest
resource: "http://mealie:9000/api/groups/mealplans/today"
method: GET
name: Mealie todays meal
headers:
Authorization: Bearer <<API_TOKEN>>
value_template: "{{ value_json[0].recipe.name }}"
force_update: true
scan_interval: 30
```
```yaml
- platform: rest
resource: "http://mealie:9000/api/groups/mealplans/today"
method: GET
name: Mealie todays meal ID
headers:
Authorization: Bearer <<API_TOKEN>>
value_template: "{{ value_json[0].recipe.id }}"
force_update: true
scan_interval: 30
```
#### 3. Create a Camera Entity
We will create a camera entity to display the image of today's meal in Lovelace.
In Home Assistant's `Integrations` page, create a new `generic camera` entity.
In the still image url field put in:
`http://mealie:9000/api/media/recipes/{{states('sensor.mealie_todays_meal_id')}}/images/min-original.webp`
Under the entity page for the new camera, rename it.
e.g. `camera.mealie_todays_meal_image`
#### 4. Create a Lovelace Card
Create a picture entity card and set the entity to `mealie_todays_meal` and the camera entity to `camera.mealie_todays_meal_image` or set in the yaml directly.
```yaml
show_state: true
show_name: true
camera_view: auto
type: picture-entity
entity: sensor.mealie_todays_meal
name: Dinner Tonight
show_state: true
show_name: true
image: 'http://localhost:9000/api/meal-plans/today/image?group_name=Home'
style:
.: |
camera_image: camera.mealie_todays_meal_image
card_mod:
style: |
ha-card {
max-height: 300px !important;
overflow: hidden;
@@ -29,19 +81,5 @@ style:
}
```
The sensor that gets the name of the meal can be achieved using the following REST sensor in Home Assistant
```yaml
sensor:
- platform: rest
resource: 'http://localhost:9000/api/meal-plans/today'
method: GET
name: Mealie todays meal
headers:
Authorization: Bearer MySuperSecretBearerCode
value_template: "{{ value_json.name }}"
```
The Bearer token can be created from the User Settings page (https://hay-kot.github.io/mealie/documentation/users-groups/user-settings/#api-key-generation)
!!! tip
Due to how Home Assistant works with images, I had to include the additional styling to get the images to not appear distorted. This includes and [additional installation](https://github.com/thomasloven/lovelace-card-mod) from HACS.
Due to how Home Assistant works with images, I had to include the additional styling to get the images to not appear distorted. This requires an [additional installation](https://github.com/thomasloven/lovelace-card-mod) from HACS.

View File

@@ -0,0 +1,18 @@
<!-- prettier-ignore -->
!!! info
This guide was submitted by a community member. Find something wrong? Submit a PR to get it fixed!
You can use bookmarklets to generate a bookmark that will take your current location, and open a new tab that will try to import that URL into Mealie.
You can use a [bookmarklet generator site](https://caiorss.github.io/bookmarklet-maker/) and the code below to generate a bookmark for your site. Just change the `http://localhost:8080` to your sites web address and follow the instructions.
<!-- prettier-ignore -->
!!! note
There is no trailing `/` at the end of the url!
```js
var url = document.URL;
var mealie = "http://localhost:8080";
var dest = mealie + "/recipe/create/url?recipe_import_url=" + url;
window.open(dest, "_blank");
```

View File

@@ -5,20 +5,35 @@
![Image from apple site](https://help.apple.com/assets/5E8CEA35094622DF10489984/5E8CEA42094622DF1048998D/en_US/ed1f9c157cdefc13e0161e0f70015455.png)
User [brasilikum](https://github.com/brasilikum) opened an issue on the main repo about how they had created an [iOS shortcut](https://github.com/hay-kot/mealie/issues/103) for interested users. This is a useful utility for iOS users who browse for recipes in their web browser from their devices. Recent updates to Mealie has made this original shortcut break. Reddit user [BooNooBooNooB](https://www.reddit.com/user/BooNooBooNooB/) has helped to create a new working version.
User [brasilikum](https://github.com/brasilikum) opened an issue on the main repo about how they had created an [iOS shortcut](https://github.com/hay-kot/mealie/issues/103) for interested users.
This original method broke after the transition to version 1.X and an issue was raised on [Github](https://github.com/hay-kot/mealie/issues/2092) GitHub user [Zippyy](https://github.com/zippyy) has helped to create a working shortcut for version 1.X.
This is a useful utility for iOS users who browse for recipes in their web browser from their devices.
Don't know what an iOS shortcut is? Neither did I! Experienced iOS users may already be familiar with this utility but for the uninitiated, here is the official Apple explanation:
> A shortcut is a quick way to get one or more tasks done with your apps. The Shortcuts app lets you create your own shortcuts with multiple steps. For example, build a “Surf Time” shortcut that grabs the surf report, gives an ETA to the beach, and launches your surf music playlist.
Basically it is a visual scripting language that lets a user build an automation in a guided fashion. The automation can be [shared with anyone](https://www.icloud.com/shortcuts/4c40fcc6f39549f9a189995a449cd44f) but if it is a user creation, you'll have to jump through a few hoops to make an untrusted automation work on your device.
Basically it is a visual scripting language that lets a user build an automation in a guided fashion. The automation can be [shared with anyone](https://www.icloud.com/shortcuts/cc568d1615bc4f998789f85d1ef74846) but if it is a user creation, you'll have to jump through a few hoops to make an untrusted automation work on your device.
You need to replace `username` and `password` with the login information for your mealie instance.
This guide assumes that you already know how to [generate API tokens](https://hay-kot.github.io/mealie/documentation/users-groups/user-settings/#api-key-generation) for your user that intends to use an iOS shortcut.
![screenshot](../../assets/img/ios-shortcut-image.jpg)
First, click the [link](https://www.icloud.com/shortcuts/cc568d1615bc4f998789f85d1ef74846) and begin the setup of the shortcut.
Then, you need to put in your mealie domain. The API port of `:9000` is not needed when putting your domain in the text field.
![screenshot](../img/iOS_host.jpg)
![screenshot](../../assets/img/sc1half.png)
Next, you need to replace `url` and `port` with the information for your mealie instance.
If you have a TLD that you use, put that here with no port. If you just run local, Then, you need to put in your mealie instance IP and the port of `9926`.
![screenshot](../../assets/img/sc2half.png)
Finally, you need to replace the word `keyhere` with your API token. Keep the word `Bearer`!!!
![screenshot](../../assets/img/sc3half.png)
You should now be able to share a website to the shortcut and have mealie grab all the necessary information!

View File

@@ -2,13 +2,12 @@
## Getting a Token
Mealie supports long-live api tokens in the user frontend. See [user settings page](../users-groups/user-settings.md)
Mealie supports long-live api tokens in the user frontend. These can be created on the `/user/profile/api-tokens` page.
## 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://demo.mealie.io/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://<your-mealie-site>/docs` or see the example at the [Demo Site](https://demo.mealie.io/docs).
### Extras
#### Recipe Extras
@@ -73,6 +72,33 @@ This filter will find all recipes created on or after a particular date: <br>
This filter will find all units that have `useAbbreviation` disabled: <br>
`useAbbreviation = false`
This filter will find all foods that are not named "carrot": <br>
`name <> "carrot"`
##### Keyword Filters
The API supports many SQL keywords, such as `IS NULL` and `IN`, as well as their negations (e.g. `IS NOT NULL` and `NOT IN`).
Here is an example of a filter that returns all recipes where the "last made" value is not null: <br>
`lastMade IS NOT NULL`
This filter will find all recipes that don't start with the word "Test": <br>
`name NOT LIKE "Test%"`
> **_NOTE:_** for more information on this, [check out the SQL "LIKE" operator](https://www.w3schools.com/sql/sql_like.asp)
This filter will find all recipes that have particular slugs: <br>
`slug IN ["pasta-fagioli", "delicious-ramen"]`
##### Nested Property filters
When querying tables with relationships, you can filter properties on related tables. For instance, if you want to query all recipes owned by a particular user: <br>
`user.username = "SousChef20220320"`
This timeline event filter will return all timeline events for recipes that were created after a particular date: <br>
`recipe.createdAt >= "2023-02-25"`
This recipe filter will return all recipes that contains a particular set of tags: <br>
`tags.name CONTAINS ALL ["Easy", "Cajun"]`
##### Compound Filters
You can combine multiple filter statements using logical operators (`AND`, `OR`).
@@ -90,3 +116,31 @@ You can have multiple filter groups combined by logical operators. You can defin
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"`
#### Advanced Ordering
Pagination supports `orderBy`, `orderByNullPosition`, and `orderDirection` params to change how you want your query results to be ordered. These can be fine-tuned for more advanced use-cases.
##### Order By
The pagination `orderBy` attribute allows you to sort your query results by a particular attribute. Sometimes, however, [you may want to sort by more than one attribute](https://www.w3schools.com/sql/sql_orderby.asp). This can be achieved by passing a comma-separated string to the `orderBy` parameter. For instance, if you want to sort recipes by their last made datetime, then by their created datetime, you can pass the following `orderBy` string: <br>
`lastMade, createdAt`
Similar to the standard SQL `ORDER BY` logic, your attribute orders will be applied sequentially. In the above example, *first* recipes will be sorted by `lastMade`, *then* any recipes with an identical `lastMade` value are sorted by `createdAt`. In addition, standard SQL rules apply when handling results with null values (such as when joining related tables). You can apply the `NULLS FIRST` and `NULLS LAST` SQL expressions by setting the `orderByNullPosition` to "first" or "last". If left empty, the default SQL behavior is applied, [which is different depending on which database you're using](https://learnsql.com/blog/how-to-order-rows-with-nulls/).
##### Order Direction
The query will be ordered in ascending or descending order, depending on what you pass to the pagination `orderDirection` param. You can either specify "asc" or "desc".
When sorting by multiple attributes, if you *also* want one or more of those sorts to be different directions, you can specify them with a colon. For instance, if, like our previous example, say you want to sort by `lastMade` and `createdAt`. However, this time, you want to sort by `lastMade` ascending, but `createdAt` descending. You could pass this `orderBy` string: <br>
`lastMade:asc, createdAt:desc`
In the above example, whatever you pass to `orderDirection` will be ignored. If, however, you only specify the direction on one attribute, all other attributes will use the `orderDirection` value.
Consider this `orderBy` string: <br>
`lastMade:asc, createdAt, slug`
And this `orderDirection` value: <br>
`desc`
This will result in a recipe query where all recipes are sorted by `lastMade` ascending, then `createdAt` descending, and finally `slug` descending.
Similar to query filters, when querying tables with relationships, you can order by properties on related tables. For instance, if you want to query all foods with labels, sorted by label name, you could use this `orderBy` value: <br>
`label.name`

View File

@@ -1,35 +1,62 @@
# Frequently Asked Questions
## How do I enable "smart" ingredient handling?
You might have noticed that scaling up a recipe or making a shopping list doesn't by default handle the ingredients in a way you might expect. Depending on your settings, scaling up might yield things like `2 1 cup broth` instead of `2 cup broth`. And making shopping lists from reciepes that have shared ingredients can yield multiple lines of the same ingredient. **But** mealie has a mechanism to intelligently handle ingredients and make your day better. How?
### Set up your Foods and Units
Do the following just **once**. Doing this applies to your whole group, so be careful.
1. Click on your name in the upper left corner to get to your settings
2. In the bottom right, select `Manage Data`
3. In the Management page, make sure that a little orange button says `Foods`
4. If your Foods database is empty, click `Seed` and choose your language. You should end up with a list of foods. (Wait bit for seeding to happen, and try not to seed more than once or you will have duplicates)
5. Click the little orange `Foods` button and now choose `Units`.
6. Click `Seed` and choose your language. You should end up with a list of units (e.g. `tablespoon`)
Initial seeding of Units is pretty complete, but there are many Foods in the world. You'll probably find that you need to add Foods to the database during parsing for the first several recipes. Once you have a well-populated Food database, there are API routes to parse ingredients automatically in bulk. But this is not a good idea without a very complete set of Foods.
### Set up Recipes to use Foods and Units
Do the following for each recipe you want to intelligently handle ingredients.
1. Go to a recipe
2. Click the Edit button/icon
3. Click the Recipe Settings gear and deselect `Disable Ingredient Amounts`
4. Save
5. The ingredients should now look a little weird (`1 1 cup broth` and so on)
6. Click the Edit button/icon again
7. Scroll to the ingredients and you should see new fields for Amount, Unit, Food, and Note. The Note in particular will contain the original text of the Recipe.
8. Click `Parse` and you will be taken to the ingredient parsing page.
9. Choose your parser. the `Natural Language Parser` works very well, but you can also use the `Brute Parser`.
10. Click `Parse All` and your ingredients should be separated out into Units and Foods based on your seeding in Step 1 above.
11. For ingredients where the Unit or Food were not found, you can click a button to accept an automatically suggested Food to add to the database. Or manually enter the Unit/Food and hit `Enter` (or click `Create`) to add it to the database
12. When done, click `Save All` and you will be taken back to the recipe. Now the Unit and Food fields of the recipe should be filled out.
Scaling up this recipe or adding it to a Shopping List will now smartly take care of ingredient amounts and duplicate combinations.
## Is it Safe to Upgrade Mealie?
Yes. If you are using the v1 branches (including beta), you can upgrade to the latest version of Mealie without performing a site Export/Restore. This process was required in previous versions of Mealie, however we've automated the database migration process to make it easier to upgrade. Not that if you were using the v0.5.x version, you CANNOT upgrade to the latest version automatically. You must follow the migration instructions in the documentation.
**Links**
- [Migration From v0.5.x](./migrating-to-mealie-v1.md)
## How can I change the theme?
You can change the theme by settings the environment variables on the frontend container.
Links:
- [Frontend Theme](./installation/frontend-config#themeing)
- [Frontend Theme](../installation/frontend-config#themeing)
## How can I change the language?
Languages need to be set on the frontend and backend containers as ENV variables.
Links
- [Frontend Config](./installation/frontend-config/)
- [Backend Config](./installation/backend-config/)
- [Frontend Config](../installation/frontend-config/)
- [Backend Config](../installation/backend-config/)
## How can I change the Login Session Timeout?
Login session can be configured by setting the `TOKEN_TIME` variable on the backend container.
- [Backend Config](./installation/backend-config/)
- [Backend Config](../installation/backend-config/)
## Can I serve Mealie on a subpath?
@@ -39,12 +66,88 @@ No. Due to limitations from the Javascript Framework, mealie doesn't support ser
Yes, you can install Mealie on your local machine. HOWEVER, it is recommended that you don't. Managing non-system versions of python, node, and npm is a pain. Moreover updating and upgrading your system with this configuration is unsupported and will likely require manual interventions. If you insist on installing Mealie on your local machine, you can use the links below to help guide your path.
- [Advanced Installation](./installation/advanced/)
- [Advanced Installation](../installation/advanced/)
## How I can attach an Image or Video to a Recipe?
## What is fuzzy search and how do I use it?
Mealie can use fuzzy search, which is robust to minor typos. For example, searching for "brocolli" will still find your recipe for "broccoli soup". But fuzzy search is only functional on a Postgres database backend. To enable fuzzy search you will need to migrate to Postgres:
1. Backup your database and download the .zip file (same as when [migrating](./migrating-to-mealie-v1.md))
2. Set up a [Postgres](./installation/postgres.md) instance of Mealie
3. Upload the backup .zip and click to apply it (as as migration)
## How i can attach an image or video to a Recipe?
Yes. Mealie's Recipe Steps and other fields support the markdown syntax and therefor supports images and videos. To attach an image to the recipe, you can upload it as an asset and use the provided copy button to generate the html image tag required to render the image. For videos, Mealie provides no way to host videos. You'll need to host your videos with another provider and embed them in your recipe. Generally, the video provider will provide a link to the video and the html tag required to render the video. For example, youtube provides the following link that works inside a step. You can adjust the width and height attributes as necessary to ensure a fit.
```html
<iframe width="560" height="315" src="https://www.youtube.com/embed/nAUwKeO93bY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
```
## How can I unlock my account?
If your account has been locked by bad password attempts, you can use an administrator account to unlock another account. Alternatively you can unlock all account via a scripts within the container.
```shell
docker exec -it mealie-next bash
python /app/mealie/scripts/reset_locked_users.py
```
## How can I change my password
You can change your password by going to the user profile page and clicking the "Change Password" button. Alternatively you can use the following script to change your password via the CLI if you are locked out of your account.
```shell
docker exec -it mealie-next bash
python /app/mealie/scripts/change_password.py
```
## How do private groups and recipes work?
Managing private groups and recipes can be confusing. The following diagram and notes should help explain how they work to determine if a recipe can be shared publicly.
- Private links that are generated using th`Share` button bypass all group and recipe permissions.
- Private groups block all access to recipes, including those that are public. Expect as noted above.
- Private recipes block all access to the recipe from public links. This does not affect Private Links.
```mermaid
stateDiagram-v2
r1: Request Access
p1: Using Private Link?
p2: Is Group Private?
p3: Is Recipe Private?
s1: Deny Access
n1: Allow Access
r1 --> p1
p1 --> p2: No
p1 --> n1: Yes
p2 --> s1: Yes
p2 --> p3: No
p3 --> s1: Yes
p3 --> n1: No
```
## Can I use fail2ban with mealie?
Yes, mealie is configured to properly forward external IP addresses into the `mealie.log` logfile. Note that, due to restrictions in docker, IP address forwarding only works on linux.
Your fail2ban usage should look like the following:
```
Use datepattern : %d-%b-%y %H:%M:%S : Day-MON-Year2 24hour:Minute:Second
Use failregex line : ^ERROR:\s+Incorrect username or password from <HOST>
```
## Why An API?
An API allows integration into applications like [Home Assistant](https://www.home-assistant.io/) that can act as notification engines to provide custom notifications based of Meal Plan data to remind you to defrost the chicken, marinade the steak, or start the CrockPot. Additionally, you can access nearly any backend service via the API giving you total control to extend the application. To explore the API spin up your server and navigate to http://yourserver.com/docs for interactive API documentation.
## Why a Database?
Some users of static-site generator applications like ChowDown have expressed concerns about their data being stuck in a database. Considering this is a new project it is a valid concern to be worried about your data. Mealie specifically addresses this concern by provided automatic daily backups that export your data in json, plain-text markdown files, and/or custom Jinja2 templates. **This puts you in controls of how your data is represented** when exported from Mealie, which means you can easily migrate to any other service provided Mealie doesn't work for you.
As to why we need a database?
- **Developer Experience:** Without a database a lot of the work to maintain your data is taken on by the developer instead of a battle tested platform for storing data.
- **Multi User Support:** With a solid database as backend storage for your data Mealie can better support multi-user sites and avoid read/write access errors when multiple actions are taken at the same time.

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://demo.mealie.io/recipe/create/url){ .md-button .md-button--primary .align-right }
[Creation 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://demo.mealie.io/group/data/foods){ .md-button .md-button--primary }
[Import Demo](https://demo.mealie.io/group/migrations){ .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://demo.mealie.io/recipes/categories){ .md-button .md-button--primary }
[Categories 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://demo.mealie.io/recipes/tags){ .md-button .md-button--primary }
[Tags Demo](https://demo.mealie.io/recipes/tags){ .md-button .md-button--primary }
#### Tools
@@ -48,7 +48,7 @@ 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://demo.mealie.io/recipes/tools){ .md-button .md-button--primary }
[Tools Demo](https://demo.mealie.io/recipes/tools){ .md-button .md-button--primary }
#### Cookbooks
@@ -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://demo.mealie.io/group/cookbooks){ .md-button .md-button--primary }
[Cookbooks 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://demo.mealie.io/group/mealplan/planner){ .md-button .md-button--primary }
[Mealplanner Demo](https://demo.mealie.io/group/mealplan/planner/view){ .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://demo.mealie.io/group/mealplan/settings){ .md-button .md-button--primary }
[Planner Settings 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://demo.mealie.io/shopping-lists){ .md-button .md-button--primary }
[Shopping List Demo](https://demo.mealie.io/shopping-lists){ .md-button .md-button--primary }
## Data Management
@@ -107,7 +107,7 @@ 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://demo.mealie.io/group/data/foods){ .md-button .md-button--primary }
[Data Management Demo](https://demo.mealie.io/group/data/foods){ .md-button .md-button--primary }
## Server Administration
@@ -115,20 +115,20 @@ Managing a robust collection of recipes inevitable requires a lot of data. Meali
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://demo.mealie.io/admin/site-settings){ .md-button .md-button--primary }
[Settings 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://demo.mealie.io/admin/manage/users){ .md-button .md-button--primary }
[Users 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://demo.mealie.io/admin/backups){ .md-button .md-button--primary }
[Backups Demo](https://demo.mealie.io/admin/backups){ .md-button .md-button--primary }
!!! note

View File

@@ -9,7 +9,7 @@
| PUID | 911 | UserID permissions between host OS and container |
| PGID | 911 | GroupID permissions between host OS and container |
| DEFAULT_GROUP | Home | The default group for users |
| DEFAULT_EMAIL | changeme@email.com | The default username for the superuser |
| DEFAULT_EMAIL | changeme@example.com | The default username for the superuser |
| BASE_URL | http://localhost:8080 | Used for Notifications |
| TOKEN_TIME | 48 | The time in hours that a login/auth token is valid |
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
@@ -17,7 +17,6 @@
| TZ | UTC | Must be set to get correct date/time on the server |
| ALLOW_SIGNUP | true | Allow user sign-up without token (should match frontend env) |
### Security
| Variables | Default | Description |
@@ -36,7 +35,6 @@
| POSTGRES_PORT | 5432 | Postgres database port |
| POSTGRES_DB | mealie | Postgres database name |
### Email
| Variables | Default | Description |
@@ -50,6 +48,7 @@
| SMTP_PASSWORD | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' |
### Webworker
Changing the webworker settings may cause unforeseen memory leak issues with Mealie. It's best to leave these at the defaults unless you begin to experience issues with multiple users. Exercise caution when changing these settings
| Variables | Default | Description |
@@ -59,15 +58,20 @@ Changing the webworker settings may cause unforeseen memory leak issues with Mea
| 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] |
### 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_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)`) |
| 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_ENABLE_STARTTLS | False | Optional. Use STARTTLS to connect to the server |
| LDAP_BASE_DN | None | Starting point when searching for users authentication (e.g. `CN=Users,DC=xx,DC=yy,DC=de`) |
| LDAP_QUERY_BIND | None | Optional bind user for LDAP search queries (e.g. `cn=admin,cn=users,dc=example,dc=com`). If `None` then anonymous bind will be used |
| LDAP_QUERY_PASSWORD | None | Optional password for the bind user used in LDAP_QUERY_BIND |
| LDAP_USER_FILTER | None | Optional LDAP filter to narrow down eligible users (e.g. `(memberOf=cn=mealie_user,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)`) |
| LDAP_ID_ATTRIBUTE | uid | The LDAP attribute that maps to the user's id |
| LDAP_NAME_ATTRIBUTE | name | The LDAP attribute that maps to the user's name |
| LDAP_MAIL_ATTRIBUTE | mail | The LDAP attribute that maps to the user's email |

View File

@@ -1,29 +0,0 @@
# Frontend Configuration
## Environment Variables
### General
| Variables | Default | Description |
| --------- | :--------------------: | ------------------------- |
| API_URL | http://mealie-api:9000 | URL to proxy API requests |
### Themeing
Setting the following environmental variables will change the theme of the frontend. Note that the themes are the same for all users. This is a break-change when migration from v0.x.x -> 1.x.x.
| Variables | Default | Description |
| --------------------- | :-----: | --------------------------- |
| THEME_LIGHT_PRIMARY | #E58325 | Light Theme Config Variable |
| THEME_LIGHT_ACCENT | #007A99 | Light Theme Config Variable |
| THEME_LIGHT_SECONDARY | #973542 | Light Theme Config Variable |
| THEME_LIGHT_SUCCESS | #43A047 | Light Theme Config Variable |
| THEME_LIGHT_INFO | #1976D2 | Light Theme Config Variable |
| THEME_LIGHT_WARNING | #FF6D00 | Light Theme Config Variable |
| THEME_LIGHT_ERROR | #EF5350 | Light 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

@@ -7,6 +7,7 @@ To install Mealie on your server there are a few steps for proper configuration.
- [SQLite docker-compose](./sqlite.md)
- [Postgres docker-compose](./postgres.md)
- [Single container docker-compose](./single-container.md)
## Pre-work
@@ -25,9 +26,21 @@ To deploy mealie on your local network it is highly recommended to use docker to
!!! warning "32bit Support"
Due to a build dependency limitation, Mealie is not supported on 32bit ARM systems. If you're running into this limitation on a newer Raspberry Pi, please consider upgrading to a 64bit operating system on the Raspberry Pi.
## Migrating From over V1 Versions
We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers *or* the omni image) to the new nightly, there are a few things you need to do:
1. Take a backup just in case!
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:nightly`
3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access.
4. Restart the container
For an example of what these changes look like, see the new [SQLite](./sqlite.md) or [PostgreSQL](./postgres.md) docker-compose examples. The container swap should be seemless, at least that's our hope!
## Step 1: Deployment Type
SQLite is a popular, open source, self-contained, zero-configuration database that is the ideal choice for Mealie when you have 1-20 Users and your concurrent write operations will be some-what limited. If you need to support many concurrent users, you may want to consider a more robust database such as PostgreSQL.
SQLite is a popular, open source, self-contained, zero-configuration database that is the ideal choice for Mealie when you have 1-20 Users and your concurrent write operations will be some-what limited.
PostgreSQL might be considered if you need to support many concurrent users. In addition, some features are only enabled on PostgreSQL, such as fuzzy search.
You can find the relevant ready to use docker-compose files for supported installations at the links below.
@@ -40,21 +53,19 @@ The following steps were tested on a Ubuntu 20.04 server, but should work for mo
1. SSH into your server and navigate to the home directory of the user you want to run Mealie as. If that is your current user, you can use `cd ~` to ensure you're in the right directory.
2. Create a directory called `docker` and navigate into it. `mkdir docker && cd docker`
3. Do the same for mealie `mkdir mealie && cd mealie`
4. Create a docker-compose.yaml file in the mealie directory. `touch docker-compose.yaml`
5. Use the text editor or your choice to edit the file and copy the contents of the docker-compose template for the deployment type you want to use. `nano docker-compose.yaml` or `vi docker-compose.yaml`
2. Create a directory called `docker` and navigate into it: `mkdir docker && cd docker` (this is optional, if you organizer your docker installs separate from everything else)
3. Do the same for mealie: `mkdir mealie && cd mealie`
4. Create a docker-compose.yaml file in the mealie directory: `touch docker-compose.yaml`
5. Use the text editor or your choice to edit the file and copy the contents of the docker-compose template for the deployment type you want to use: `nano docker-compose.yaml` or `vi docker-compose.yaml`
## Step 2: Customizing The `docker-compose.yaml` files.
After you've decided setup the files it's important to set a few ENV variables to ensure that you can use all the features of Mealie. I recommend that you verify and check that:
- [x] You've configured the relevant ENV variables for your database selection in the `docker-compose.yaml` files.
- [x] You've configured the [SMTP server settings](./backend-config.md#email) (used for invitations, password resets, etc)
- [x] Verified the port mapped on the `mealie-frontend` container is an open port on your server (Default: 9925)
- [x] You've configured the [SMTP server settings](./backend-config.md#email) (used for invitations, password resets, etc). You can setup a [google app password](https://support.google.com/accounts/answer/185833?hl=en) if you want to send email via gmail.
- [x] You've set the [`BASE_URL`](./backend-config.md#general) variable.
- [x] You've set the `DEFAULT_EMAIL` and `DEFAULT_GROUP` variable.
- [x] Make any theme changes on the frontend container. [See Frontend Config](./frontend-config.md#themeing)
## Step 3: Startup
After you've configured your database, and updated the `docker-compose.yaml` files, you can start Mealie by running the following command in the directory where you've added your `docker-compose.yaml`.
@@ -65,9 +76,13 @@ $ 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://localhost:9925).
!!! warning "Default Username"
Note that the default username (below) has been changed from previous versions
!!! tip "Default Credentials"
**Username:** changeme@email.com
**Username:** changeme@example.com
**Password:** MyPassword
@@ -75,9 +90,6 @@ You should see the containers start up without error. You should now be able to
After the startup is complete you should see a login screen. Use the default credentials above to login and navigate to `/admin/site-settings`. Here you'll find a summary of your configuration details and their respective status. Before proceeding you should validate that the configuration is correct. For any warnings or errors the page will display an error and notify you of what you need to verify.
!!! tip "Docker Volume"
Mealie uses a shared data-volume between the Backend and Frontend containers for images and assets. Ensure that this is configured correctly by using the "Docker Volume Test" section in the settings page. Running this validation will ensure that you have configured your volumes correctly. Mealie will not work correctly without this configured correctly.
## Step 5: Backup
While v1.0.0 is a great step to data-stability and security, it's not a backup. Mealie provides a full site data backup mechanism through the UI.
@@ -87,6 +99,14 @@ These backups are just plain .zip files that you can download from the UI or acc
### Docker Tags
`ghcr.io/mealie-recipes/mealie:nightly`
The nightly build are the latest and greatest builds that are built directly off of every commit to the `mealie-next` branch and as such may contain bugs. These are great to help the community catch bugs before they hit the stable release or if you like living on the edge.
---
**These tags no are long updated**
`mealie:frontend-v1.0.0beta-x` **and** `mealie:api-v1.0.0beta-x`
These are the tags for the latest beta release of the frontend docker-container. These are currently considered the latest and most stable releases and the recommended way of using Mealie.
@@ -94,16 +114,3 @@ These are the tags for the latest beta release of the frontend docker-container.
`mealie:frontend-nightly`**and** `mealie:api-nightly`
The nightly build are the latest and greatest builds that are built directly off of every commit to the `mealie-next` branch and as such may contain bugs. These are great to help the community catch bugs before they hit the stable release or if you like living on the edge.
### Docker Diagram
While the docker-compose file should work without modification, some users want to tailor it to their installation. This diagram shows network and volume architecture for the default setup. You can use this to help you customize your configuration.
![Docker Diagram](../../../assets/img/docker-diagram.drawio.svg)
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 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

@@ -1,34 +1,24 @@
# Installing with PostgreSQL
PostgreSQL might be considered if you need to support many concurrent users. In addition, some features are only enabled on PostgreSQL, such as fuzzy search.
**For Environmental Variable Configuration See:**
- [Frontend Configuration](./frontend-config.md)
- [Backend Configuration](./backend-config.md)
- [Configuration](./backend-config.md)
```yaml
---
version: "3.7"
services:
mealie-frontend:
image: hkotel/mealie:frontend-v1.0.0beta-4
container_name: mealie-frontend
depends_on:
- mealie-api
environment:
# Set Frontend ENV Variables Here
- API_URL=http://mealie-api:9000 # (1)
restart: always
mealie:
image: ghcr.io/mealie-recipes/mealie:nightly
container_name: mealie
ports:
- "9925:3000" # (2)
volumes:
- mealie-data:/app/data/ # (3)
mealie-api:
image: hkotel/mealie:api-v1.0.0beta-4
container_name: mealie-api
- "9925:9000"
deploy:
resources:
limits:
memory: 1000M # (4)
memory: 1000M # (1)
depends_on:
- postgres
volumes:
@@ -53,8 +43,10 @@ services:
restart: always
postgres:
container_name: postgres
image: postgres
image: postgres:15
restart: always
volumes:
- ./mealie-pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: mealie
POSTGRES_USER: mealie
@@ -62,12 +54,11 @@ services:
volumes:
mealie-data:
driver: local
mealie-pgdata:
driver: local
```
<!-- Updating This? Be Sure to also update the SQLite Annotations -->
1. Whoa whoa whoa, what is this nonsense? The API_URL is the URL the frontend container uses to proxy api requests to the backend server. In this example, the name `mealie-api` resolves to the `mealie-api` container which runs the API server on port 9000. This allows you to access the API without exposing an additional port on the host.
<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.
1. To access the mealie interface you only need to expose port 9000 on the mealie container. Here we expose port 9925 on the host, but feel free to change this to any port you like.
2. 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,31 +4,21 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
**For Environmental Variable Configuration See:**
- [Frontend Configuration](./frontend-config.md)
- [Backend Configuration](./backend-config.md)
- [Configuration](./backend-config.md)
```yaml
---
version: "3.7"
services:
mealie-frontend:
image: hkotel/mealie:frontend-v1.0.0beta-4
container_name: mealie-frontend
environment:
# Set Frontend ENV Variables Here
- API_URL=http://mealie-api:9000 # (1)
restart: always
mealie:
image: ghcr.io/mealie-recipes/mealie:nightly
container_name: mealie
ports:
- "9925:3000" # (2)
volumes:
- mealie-data:/app/data/ # (3)
mealie-api:
image: hkotel/mealie:api-v1.0.0beta-4
container_name: mealie-api
- "9925:9000" # (1)
deploy:
resources:
limits:
memory: 1000M # (4)
memory: 1000M # (2)
volumes:
- mealie-data:/app/data/
environment:
@@ -49,8 +39,5 @@ volumes:
<!-- Updating This? Be Sure to also update the Postgres Annotations -->
1. Whoa whoa whoa, what is this nonsense? The API_URL is the URL the frontend container uses to proxy api requests to the backend server. In this example, the name `mealie-api` resolves to the `mealie-api` container which runs the API server on port 9000. This allows you to access the API without exposing an additional port on the host.
<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.
1. To access the mealie interface you only need to expose port 9000 on the container. Here we expose port 9925 on the host, but feel free to change this to any port you like.
2. 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

@@ -12,14 +12,14 @@ Mealie is a self hosted recipe manager and meal planner with a RestAPI backend a
## Key Features
- 🔍 Fuzzy search
- 🏷️ Tag recipes with categories or tags to flexible sorting
- 🔍 Smart search, mix & match of "quoted literal searches" and keyword search. Fuzzy search ("is it brocolli or broccoli?") is also available when using a Postgres database.
- 🏷️ Tag recipes with categories or tags for flexible sorting
- 🕸 Import recipes from around the web by URL
- 📱 Progressive Web App
- 📆 Create Meal Plans
- 🛒 Generate shopping lists
- 🐳 Easy setup with Docker
- 🎨 Customize your interface with color themes layouts
- 🎨 Customize your interface with color themed layouts
- 💾 Export all your data in any format with Jinja2 Templates, with easy data restoration from the user interface.
- 🌍 localized in many languages
- Plus tons more!
@@ -34,17 +34,8 @@ Mealie is a self hosted recipe manager and meal planner with a RestAPI backend a
- Random meal plan generation
## FAQ
See the [Frequently Asked Questions page](./faq.md)
### Why An API?
An API allows integration into applications like [Home Assistant](https://www.home-assistant.io/) that can act as notification engines to provide custom notifications based of Meal Plan data to remind you to defrost the chicken, marinade the steak, or start the CrockPot. Additionally, you can access nearly any backend service via the API giving you total control to extend the application. To explore the API spin up your server and navigate to http://yourserver.com/docs for interactive API documentation.
### Why a Database?
Some users of static-site generator applications like ChowDown have expressed concerns about their data being stuck in a database. Considering this is a new project it is a valid concern to be worried about your data. Mealie specifically addresses this concern by provided automatic daily backups that export your data in json, plain-text markdown files, and/or custom Jinja2 templates. **This puts you in controls of how your data is represented** when exported from Mealie, which means you can easily migrate to any other service provided Mealie doesn't work for you.
As to why we need a database?
- **Developer Experience:** Without a database a lot of the work to maintain your data is taken on by the developer instead of a battle tested platform for storing data.
- **Multi User Support:** With a solid database as backend storage for your data Mealie can better support multi-user sites and avoid read/write access errors when multiple actions are taken at the same time.
## Built With
@@ -53,11 +44,6 @@ As to why we need a database?
* [FastAPI](https://fastapi.tiangolo.com/)
* [Docker](https://www.docker.com/)
<!-- ROADMAP -->
## Road Map
[See Roadmap](../../roadmap.md)
<!-- CONTRIBUTING -->
## Contributing

View File

@@ -54,4 +54,4 @@ In most cases, it's faster to manually migrate the recipes that didn't take inst
v1 Comes with a whole host of new features and improvements. Checkout the changelog to get a sense for what's new.
- [v1 Changelog](../../changelog/v1.0.0.md)
- [Github releases changelog](https://github.com/hay-kot/mealie/releases)

View File

@@ -1,7 +1,7 @@
# Development Road Map
## Feature Requests
See the [Github META issue for tracking feature requests](https://github.com/hay-kot/mealie/issues/122)
[Please request new features on Github](https://github.com/hay-kot/mealie/issues/317)
## Progress
See the [Github Projects](https://github.com/hay-kot/mealie/projects) to see what is currently being worked on
## Progress
See the [Github Projects page](https://github.com/users/hay-kot/projects/2) to see what is currently being worked on

View File

@@ -1,7 +1,7 @@
# Updating Mealie
!!! warning "Read The Release Notes"
You MUST read the release notes prior to upgrading your container. Currently Mealie provides no database migrations as doing so would slow down development and hinder major changes that may need to happen prior to v1.0.0. Mealie has a robust backup and restore system for managing your data.
You MUST read the release notes prior to upgrading your container. Mealie has a robust backup and restore system for managing your data. Pre-v1.0.0 versions of Mealie use a different database structure, so if you are upgrading from pre-v1.0.0 to v1.0.0, you MUST backup your data and then re-import it. Even if you are already on v1.0.0, it is strongly recommended to backup all data before updating.
### Before Upgrading
- Read The Release Notes
@@ -9,13 +9,16 @@
- Create a Backup and Download from the UI
- Upgrade
## Upgrading to Mealie v1
If you are upgrading from pre-v1.0.0 to v1.0.0, make sure you read [Migrating to Mealie v1](./migrating-to-mealie-v1.md)!
## Backing Up Your Data
[See Backups and Restore Section](../admin/backups-and-exports.md) for details on backing up your data
[See Backups and Restore Section](../getting-started/usage/backups-and-restoring.md) for details on backing up your data
## Docker
For all setups using Docker the updating process looks something like this
- Stop the container using docker-compose down
- Pull the latest image using docker-compose pull
- Start the container again using docker-compose up -d
- Pull the latest image using `docker-compose pull`
- Start the container again using `docker-compose up -d`

View File

@@ -0,0 +1,8 @@
# LDAP Authentication
If LDAP is enabled and [configured properly](../installation/backend-config.md), users will be able to log in with their LDAP credentials. If the user does not already have an account in Mealie, then one will be created.
If the user already has an account in Mealie and wants to use their LDAP credentials instead, then you can go to the **User Management** page in the admin panel and change the "Authentication Backend" from `Mealie` to `LDAP`. If for whatever reason, the user no longer wants to use LDAP authentication, then you can switch this back to `Mealie`.
!!! warning "Head's Up"
If you switch a user from `LDAP` to `Mealie` who was initially created by LDAP, then the user will have to reset their password through the password reset flow.

File diff suppressed because one or more lines are too long

View File

@@ -224,19 +224,11 @@
and a reactive frontend application built in Vue for a pleasant user
experience for the whole family.
</p>
<a
href="{{ page.next_page.url | url }}"
title="{{ page.next_page.title | striptags }}"
class="md-button md-button--primary"
>
<a href="{{ page.next_page.url | url }}" title="{{ page.next_page.title | striptags }}"
class="md-button md-button--primary">
Get started
</a>
<a
href="{{ config.demo_url }}"
title="{{ lang.t('source.link.title') }}"
target="_blank"
class="md-button"
>
<a href="{{ config.demo_url }}" title="{{ lang.t('source.link.title') }}" target="_blank" class="md-button">
View the Demo
</a>
</div>
@@ -251,10 +243,8 @@
<div class="feature-item">
<h2>
<svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M15.5,14L20.5,19L19,20.5L14,15.5V14.71L13.73,14.43C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.43,13.73L14.71,14H15.5M9.5,4.5L8.95,4.53C8.71,5.05 8.34,5.93 8.07,7H10.93C10.66,5.93 10.29,5.05 10.05,4.53C9.87,4.5 9.69,4.5 9.5,4.5M13.83,7C13.24,5.97 12.29,5.17 11.15,4.78C11.39,5.31 11.7,6.08 11.93,7H13.83M5.17,7H7.07C7.3,6.08 7.61,5.31 7.85,4.78C6.71,5.17 5.76,5.97 5.17,7M4.5,9.5C4.5,10 4.58,10.53 4.73,11H6.87L6.75,9.5L6.87,8H4.73C4.58,8.47 4.5,9 4.5,9.5M14.27,11C14.42,10.53 14.5,10 14.5,9.5C14.5,9 14.42,8.47 14.27,8H12.13C12.21,8.5 12.25,9 12.25,9.5C12.25,10 12.21,10.5 12.13,11H14.27M7.87,8L7.75,9.5L7.87,11H11.13C11.21,10.5 11.25,10 11.25,9.5C11.25,9 11.21,8.5 11.13,8H7.87M9.5,14.5C9.68,14.5 9.86,14.5 10.03,14.47C10.28,13.95 10.66,13.07 10.93,12H8.07C8.34,13.07 8.72,13.95 8.97,14.47L9.5,14.5M13.83,12H11.93C11.7,12.92 11.39,13.69 11.15,14.22C12.29,13.83 13.24,13.03 13.83,12M5.17,12C5.76,13.03 6.71,13.83 7.85,14.22C7.61,13.69 7.3,12.92 7.07,12H5.17Z"
/>
<path fill="currentColor"
d="M15.5,14L20.5,19L19,20.5L14,15.5V14.71L13.73,14.43C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.43,13.73L14.71,14H15.5M9.5,4.5L8.95,4.53C8.71,5.05 8.34,5.93 8.07,7H10.93C10.66,5.93 10.29,5.05 10.05,4.53C9.87,4.5 9.69,4.5 9.5,4.5M13.83,7C13.24,5.97 12.29,5.17 11.15,4.78C11.39,5.31 11.7,6.08 11.93,7H13.83M5.17,7H7.07C7.3,6.08 7.61,5.31 7.85,4.78C6.71,5.17 5.76,5.97 5.17,7M4.5,9.5C4.5,10 4.58,10.53 4.73,11H6.87L6.75,9.5L6.87,8H4.73C4.58,8.47 4.5,9 4.5,9.5M14.27,11C14.42,10.53 14.5,10 14.5,9.5C14.5,9 14.42,8.47 14.27,8H12.13C12.21,8.5 12.25,9 12.25,9.5C12.25,10 12.21,10.5 12.13,11H14.27M7.87,8L7.75,9.5L7.87,11H11.13C11.21,10.5 11.25,10 11.25,9.5C11.25,9 11.21,8.5 11.13,8H7.87M9.5,14.5C9.68,14.5 9.86,14.5 10.03,14.47C10.28,13.95 10.66,13.07 10.93,12H8.07C8.34,13.07 8.72,13.95 8.97,14.47L9.5,14.5M13.83,12H11.93C11.7,12.92 11.39,13.69 11.15,14.22C12.29,13.83 13.24,13.03 13.83,12M5.17,12C5.76,13.03 6.71,13.83 7.85,14.22C7.61,13.69 7.3,12.92 7.07,12H5.17Z" />
</svg>
Import Recipes
</h2>
@@ -266,10 +256,8 @@
<div class="feature-item">
<h2>
<svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M12,3A9,9 0 0,0 3,12H0L4,16L8,12H5A7,7 0 0,1 12,5A7,7 0 0,1 19,12A7,7 0 0,1 12,19C10.5,19 9.09,18.5 7.94,17.7L6.5,19.14C8.04,20.3 9.94,21 12,21A9,9 0 0,0 21,12A9,9 0 0,0 12,3M14,12A2,2 0 0,0 12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12Z"
/>
<path fill="currentColor"
d="M12,3A9,9 0 0,0 3,12H0L4,16L8,12H5A7,7 0 0,1 12,5A7,7 0 0,1 19,12A7,7 0 0,1 12,19C10.5,19 9.09,18.5 7.94,17.7L6.5,19.14C8.04,20.3 9.94,21 12,21A9,9 0 0,0 21,12A9,9 0 0,0 12,3M14,12A2,2 0 0,0 12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12Z" />
</svg>
Automatic Backups
</h2>
@@ -281,10 +269,8 @@
<div class="feature-item">
<h2>
<svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M19,4C20.11,4 21,4.9 21,6V18A2,2 0 0,1 19,20H5C3.89,20 3,19.1 3,18V6A2,2 0 0,1 5,4H19M19,18V8H5V18H19Z"
/>
<path fill="currentColor"
d="M19,4C20.11,4 21,4.9 21,6V18A2,2 0 0,1 19,20H5C3.89,20 3,19.1 3,18V6A2,2 0 0,1 5,4H19M19,18V8H5V18H19Z" />
</svg>
Rich User Interface
</h2>
@@ -296,10 +282,8 @@
<div class="feature-item">
<h2>
<svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M9,10V12H7V10H9M13,10V12H11V10H13M17,10V12H15V10H17M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5A2,2 0 0,1 5,3H6V1H8V3H16V1H18V3H19M19,19V8H5V19H19M9,14V16H7V14H9M13,14V16H11V14H13M17,14V16H15V14H17Z"
/>
<path fill="currentColor"
d="M9,10V12H7V10H9M13,10V12H11V10H13M17,10V12H15V10H17M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5A2,2 0 0,1 5,3H6V1H8V3H16V1H18V3H19M19,19V8H5V19H19M9,14V16H7V14H9M13,14V16H11V14H13M17,14V16H15V14H17Z" />
</svg>
Meal Planner
</h2>
@@ -312,10 +296,8 @@
<div class="feature-item">
<h2>
<svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z"
/>
<path fill="currentColor"
d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" />
</svg>
Users
</h2>
@@ -327,25 +309,21 @@
<div class="feature-item">
<h2>
<svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z"
/>
<path fill="currentColor"
d="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z" />
</svg>
Groups
</h2>
<p>
Sort users into groups to share recipes with the whole family, but keep
your Meal Plans separate.
Full multi-user support with groups for sharing recipes and meal plans
with family.
</p>
</div>
<div class="feature-item">
<h2>
<svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M10.46,19C9,21.07 6.15,21.59 4.09,20.15C2.04,18.71 1.56,15.84 3,13.75C3.87,12.5 5.21,11.83 6.58,11.77L6.63,13.2C5.72,13.27 4.84,13.74 4.27,14.56C3.27,16 3.58,17.94 4.95,18.91C6.33,19.87 8.26,19.5 9.26,18.07C9.57,17.62 9.75,17.13 9.82,16.63V15.62L15.4,15.58L15.47,15.47C16,14.55 17.15,14.23 18.05,14.75C18.95,15.27 19.26,16.43 18.73,17.35C18.2,18.26 17.04,18.58 16.14,18.06C15.73,17.83 15.44,17.46 15.31,17.04L11.24,17.06C11.13,17.73 10.87,18.38 10.46,19M17.74,11.86C20.27,12.17 22.07,14.44 21.76,16.93C21.45,19.43 19.15,21.2 16.62,20.89C15.13,20.71 13.9,19.86 13.19,18.68L14.43,17.96C14.92,18.73 15.75,19.28 16.75,19.41C18.5,19.62 20.05,18.43 20.26,16.76C20.47,15.09 19.23,13.56 17.5,13.35C16.96,13.29 16.44,13.36 15.97,13.53L15.12,13.97L12.54,9.2H12.32C11.26,9.16 10.44,8.29 10.47,7.25C10.5,6.21 11.4,5.4 12.45,5.44C13.5,5.5 14.33,6.35 14.3,7.39C14.28,7.83 14.11,8.23 13.84,8.54L15.74,12.05C16.36,11.85 17.04,11.78 17.74,11.86M8.25,9.14C7.25,6.79 8.31,4.1 10.62,3.12C12.94,2.14 15.62,3.25 16.62,5.6C17.21,6.97 17.09,8.47 16.42,9.67L15.18,8.95C15.6,8.14 15.67,7.15 15.27,6.22C14.59,4.62 12.78,3.85 11.23,4.5C9.67,5.16 8.97,7 9.65,8.6C9.93,9.26 10.4,9.77 10.97,10.11L11.36,10.32L8.29,15.31C8.32,15.36 8.36,15.42 8.39,15.5C8.88,16.41 8.54,17.56 7.62,18.05C6.71,18.54 5.56,18.18 5.06,17.24C4.57,16.31 4.91,15.16 5.83,14.67C6.22,14.46 6.65,14.41 7.06,14.5L9.37,10.73C8.9,10.3 8.5,9.76 8.25,9.14Z"
/>
<path fill="currentColor"
d="M10.46,19C9,21.07 6.15,21.59 4.09,20.15C2.04,18.71 1.56,15.84 3,13.75C3.87,12.5 5.21,11.83 6.58,11.77L6.63,13.2C5.72,13.27 4.84,13.74 4.27,14.56C3.27,16 3.58,17.94 4.95,18.91C6.33,19.87 8.26,19.5 9.26,18.07C9.57,17.62 9.75,17.13 9.82,16.63V15.62L15.4,15.58L15.47,15.47C16,14.55 17.15,14.23 18.05,14.75C18.95,15.27 19.26,16.43 18.73,17.35C18.2,18.26 17.04,18.58 16.14,18.06C15.73,17.83 15.44,17.46 15.31,17.04L11.24,17.06C11.13,17.73 10.87,18.38 10.46,19M17.74,11.86C20.27,12.17 22.07,14.44 21.76,16.93C21.45,19.43 19.15,21.2 16.62,20.89C15.13,20.71 13.9,19.86 13.19,18.68L14.43,17.96C14.92,18.73 15.75,19.28 16.75,19.41C18.5,19.62 20.05,18.43 20.26,16.76C20.47,15.09 19.23,13.56 17.5,13.35C16.96,13.29 16.44,13.36 15.97,13.53L15.12,13.97L12.54,9.2H12.32C11.26,9.16 10.44,8.29 10.47,7.25C10.5,6.21 11.4,5.4 12.45,5.44C13.5,5.5 14.33,6.35 14.3,7.39C14.28,7.83 14.11,8.23 13.84,8.54L15.74,12.05C16.36,11.85 17.04,11.78 17.74,11.86M8.25,9.14C7.25,6.79 8.31,4.1 10.62,3.12C12.94,2.14 15.62,3.25 16.62,5.6C17.21,6.97 17.09,8.47 16.42,9.67L15.18,8.95C15.6,8.14 15.67,7.15 15.27,6.22C14.59,4.62 12.78,3.85 11.23,4.5C9.67,5.16 8.97,7 9.65,8.6C9.93,9.26 10.4,9.77 10.97,10.11L11.36,10.32L8.29,15.31C8.32,15.36 8.36,15.42 8.39,15.5C8.88,16.41 8.54,17.56 7.62,18.05C6.71,18.54 5.56,18.18 5.06,17.24C4.57,16.31 4.91,15.16 5.83,14.67C6.22,14.46 6.65,14.41 7.06,14.5L9.37,10.73C8.9,10.3 8.5,9.76 8.25,9.14Z" />
</svg>
Webhooks
</h2>
@@ -357,10 +335,8 @@
<div class="feature-item">
<h2>
<svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M7 7H5A2 2 0 0 0 3 9V17H5V13H7V17H9V9A2 2 0 0 0 7 7M7 11H5V9H7M14 7H10V17H12V13H14A2 2 0 0 0 16 11V9A2 2 0 0 0 14 7M14 11H12V9H14M20 9V15H21V17H17V15H18V9H17V7H21V9Z"
/>
<path fill="currentColor"
d="M7 7H5A2 2 0 0 0 3 9V17H5V13H7V17H9V9A2 2 0 0 0 7 7M7 11H5V9H7M14 7H10V17H12V13H14A2 2 0 0 0 16 11V9A2 2 0 0 0 14 7M14 11H12V9H14M20 9V15H21V17H17V15H18V9H17V7H21V9Z" />
</svg>
Open API
</h2>
@@ -375,43 +351,28 @@
<!-- Custom narrow footer -->
<div class="md-footer-meta__inner md-grid">
<div class="md-footer-social">
<a
class="md-footer-social__link"
href="https://github.com/hay-kot/mealie"
rel="noopener"
target="_blank"
title="github.com"
>
<a class="md-footer-social__link" href="https://github.com/hay-kot/mealie" rel="noopener" target="_blank"
title="github.com">
<svg style="width: 32px; height: 32px" viewBox="0 0 480 512" xmlns="http://www.w3.org/2000/svg">
<path
d="M186.1 328.7c0 20.9-10.9 55.1-36.7 55.1s-36.7-34.2-36.7-55.1 10.9-55.1 36.7-55.1 36.7 34.2 36.7 55.1zM480 278.2c0 31.9-3.2 65.7-17.5 95-37.9 76.6-142.1 74.8-216.7 74.8-75.8 0-186.2 2.7-225.6-74.8-14.6-29-20.2-63.1-20.2-95 0-41.9 13.9-81.5 41.5-113.6-5.2-15.8-7.7-32.4-7.7-48.8 0-21.5 4.9-32.3 14.6-51.8 45.3 0 74.3 9 108.8 36 29-6.9 58.8-10 88.7-10 27 0 54.2 2.9 80.4 9.2 34-26.7 63-35.2 107.8-35.2 9.8 19.5 14.6 30.3 14.6 51.8 0 16.4-2.6 32.7-7.7 48.2 27.5 32.4 39 72.3 39 114.2zm-64.3 50.5c0-43.9-26.7-82.6-73.5-82.6-18.9 0-37 3.4-56 6-14.9 2.3-29.8 3.2-45.1 3.2-15.2 0-30.1-.9-45.1-3.2-18.7-2.6-37-6-56-6-46.8 0-73.5 38.7-73.5 82.6 0 87.8 80.4 101.3 150.4 101.3h48.2c70.3 0 150.6-13.4 150.6-101.3zm-82.6-55.1c-25.8 0-36.7 34.2-36.7 55.1s10.9 55.1 36.7 55.1 36.7-34.2 36.7-55.1-10.9-55.1-36.7-55.1z"
></path>
d="M186.1 328.7c0 20.9-10.9 55.1-36.7 55.1s-36.7-34.2-36.7-55.1 10.9-55.1 36.7-55.1 36.7 34.2 36.7 55.1zM480 278.2c0 31.9-3.2 65.7-17.5 95-37.9 76.6-142.1 74.8-216.7 74.8-75.8 0-186.2 2.7-225.6-74.8-14.6-29-20.2-63.1-20.2-95 0-41.9 13.9-81.5 41.5-113.6-5.2-15.8-7.7-32.4-7.7-48.8 0-21.5 4.9-32.3 14.6-51.8 45.3 0 74.3 9 108.8 36 29-6.9 58.8-10 88.7-10 27 0 54.2 2.9 80.4 9.2 34-26.7 63-35.2 107.8-35.2 9.8 19.5 14.6 30.3 14.6 51.8 0 16.4-2.6 32.7-7.7 48.2 27.5 32.4 39 72.3 39 114.2zm-64.3 50.5c0-43.9-26.7-82.6-73.5-82.6-18.9 0-37 3.4-56 6-14.9 2.3-29.8 3.2-45.1 3.2-15.2 0-30.1-.9-45.1-3.2-18.7-2.6-37-6-56-6-46.8 0-73.5 38.7-73.5 82.6 0 87.8 80.4 101.3 150.4 101.3h48.2c70.3 0 150.6-13.4 150.6-101.3zm-82.6-55.1c-25.8 0-36.7 34.2-36.7 55.1s10.9 55.1 36.7 55.1 36.7-34.2 36.7-55.1-10.9-55.1-36.7-55.1z">
</path>
</svg>
</a>
<a
class="md-footer-social__link"
href="https://twitter.com/kot_hay"
rel="noopener"
target="_blank"
title="twitter.com"
>
<a class="md-footer-social__link" href="https://twitter.com/kot_hay" rel="noopener" target="_blank"
title="twitter.com">
<svg style="width: 32px; height: 32px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<path
d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"
></path>
d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z">
</path>
</svg>
</a>
<a
class="md-footer-social__link"
href="https://www.linkedin.com/in/hay-kot"
rel="noopener"
target="_blank"
title="www.linkedin.com"
>
<a class="md-footer-social__link" href="https://www.linkedin.com/in/hay-kot" rel="noopener" target="_blank"
title="www.linkedin.com">
<svg style="width: 32px; height: 32px" viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg">
<path
d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"
></path>
d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z">
</path>
</svg>
</a>
</div>

View File

@@ -42,8 +42,11 @@ markdown_extensions:
- admonition
- attr_list
- pymdownx.tabbed
- pymdownx.superfences
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
extra_css:
- assets/stylesheets/custom.css
extra_javascript:
@@ -62,21 +65,23 @@ nav:
- FAQ: "documentation/getting-started/faq.md"
- API: "documentation/getting-started/api-usage.md"
- Road Map: "documentation/getting-started/roadmap.md"
- Advanced: "documentation/getting-started/installation/advanced.md"
- Installation:
- Installation Checklist: "documentation/getting-started/installation/installation-checklist.md"
- SQLite (Recommended): "documentation/getting-started/installation/sqlite.md"
- 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"
- LDAP Authentication: "documentation/getting-started/usage/ldap.md"
- Community Guides:
- iOS Shortcuts: "documentation/community-guide/ios.md"
- Reverse Proxy (SWAG): "documentation/community-guide/swag.md"
- Home Assistant: "documentation/community-guide/home-assistant.md"
- Bulk Url Import: "documentation/community-guide/bulk-url-import.md"
- Import Bookmarklet: "documentation/community-guide/import-recipe-bookmarklet.md"
- API Reference: "api/redoc.md"
@@ -90,6 +95,7 @@ nav:
- Improving Ingredient Parser: "contributors/guides/ingredient-parser.md"
- Change Log:
- v1.0.0beta-5: "changelog/v1.0.0beta-5.md"
- 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"

View File

@@ -1,8 +1,5 @@
module.exports = {
root: true,
settings: {
"import/ignore": ["@vueuse*"],
},
env: {
browser: true,
node: true,
@@ -26,7 +23,7 @@ module.exports = {
],
// Re-add once we use nuxt bridge
// See https://v3.nuxtjs.org/getting-started/bridge#update-nuxtconfig
ignorePatterns: ["nuxt.config.js"],
ignorePatterns: ["nuxt.config.js", "lib/api/types/**/*.ts"],
plugins: ["prettier"],
// add your custom rules here
rules: {
@@ -58,6 +55,15 @@ module.exports = {
],
// TODO Gradually activate all rules
// Allow Promise in onMounted
"@typescript-eslint/no-misused-promises": [
"error",
{
checksVoidReturn: {
arguments: false,
},
},
],
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",

1
frontend/.nuxtignore Normal file
View File

@@ -0,0 +1 @@
pages/**/*.ts

View File

@@ -1,46 +0,0 @@
{
auto_https off
admin off
}
:3000 {
@apidocs path /docs /openapi.json
@static {
file
path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.woff2 *.webp
}
encode gzip zstd
# Handles Recipe Images / Assets
handle_path /api/media/recipes/* {
header @static Cache-Control max-age=31536000
root * /app/data/recipes/
file_server
}
# Handles User Images
handle_path /api/media/users/* {
header @static Cache-Control max-age=31536000
root * /app/data/users/
file_server
}
# Handle Docker Volume Validation File
handle_path /api/media/docker/* {
root * /app/data/docker-validation/
file_server
}
handle @apidocs {
uri strip_suffix /
reverse_proxy {$API_URL}
}
handle {
uri strip_suffix /
reverse_proxy http://127.0.0.1:3001
}
}

View File

@@ -1,38 +0,0 @@
FROM node:16 as builder
WORKDIR /app
COPY . .
RUN yarn install \
--prefer-offline \
--frozen-lockfile \
--non-interactive \
--production=false \
# https://github.com/docker/build-push-action/issues/471
--network-timeout 1000000
RUN yarn build
RUN rm -rf node_modules && \
NODE_ENV=production yarn install \
--prefer-offline \
--pure-lockfile \
--non-interactive \
--production=true
FROM node:16-alpine
RUN apk add caddy
WORKDIR /app
# copying caddy into image
COPY --from=builder /app .
COPY ./Caddyfile /app/
ENV HOST 0.0.0.0
EXPOSE 3000
RUN chmod +x /app/run.sh
ENTRYPOINT /app/run.sh

Binary file not shown.

View File

@@ -4,7 +4,7 @@
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-cyrillic-ext1.woff2') format('woff2');
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 */
@@ -13,7 +13,7 @@
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-cyrillic2.woff2') format('woff2');
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 */
@@ -22,7 +22,7 @@
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-greek-ext3.woff2') format('woff2');
src: url('~assets/fonts/Roboto-100-greek-ext3.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@@ -31,7 +31,7 @@
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-greek4.woff2') format('woff2');
src: url('~assets/fonts/Roboto-100-greek4.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@@ -40,7 +40,7 @@
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-vietnamese5.woff2') format('woff2');
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 */
@@ -49,7 +49,7 @@
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-latin-ext6.woff2') format('woff2');
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 */
@@ -58,7 +58,7 @@
font-style: normal;
font-weight: 100;
font-display: swap;
src: url('/assets/fonts/Roboto-100-latin7.woff2') format('woff2');
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 */
@@ -67,7 +67,7 @@
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-cyrillic-ext8.woff2') format('woff2');
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 */
@@ -76,7 +76,7 @@
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-cyrillic9.woff2') format('woff2');
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 */
@@ -85,7 +85,7 @@
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-greek-ext10.woff2') format('woff2');
src: url('~assets/fonts/Roboto-300-greek-ext10.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@@ -94,7 +94,7 @@
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-greek11.woff2') format('woff2');
src: url('~assets/fonts/Roboto-300-greek11.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@@ -103,7 +103,7 @@
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-vietnamese12.woff2') format('woff2');
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 */
@@ -112,7 +112,7 @@
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-latin-ext13.woff2') format('woff2');
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 */
@@ -121,7 +121,7 @@
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/assets/fonts/Roboto-300-latin14.woff2') format('woff2');
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 */
@@ -130,7 +130,7 @@
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-cyrillic-ext15.woff2') format('woff2');
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 */
@@ -139,7 +139,7 @@
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-cyrillic16.woff2') format('woff2');
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 */
@@ -148,7 +148,7 @@
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-greek-ext17.woff2') format('woff2');
src: url('~assets/fonts/Roboto-400-greek-ext17.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@@ -157,7 +157,7 @@
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-greek18.woff2') format('woff2');
src: url('~assets/fonts/Roboto-400-greek18.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@@ -166,7 +166,7 @@
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-vietnamese19.woff2') format('woff2');
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 */
@@ -175,7 +175,7 @@
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-latin-ext20.woff2') format('woff2');
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 */
@@ -184,7 +184,7 @@
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/assets/fonts/Roboto-400-latin21.woff2') format('woff2');
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 */
@@ -193,7 +193,7 @@
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-cyrillic-ext22.woff2') format('woff2');
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 */
@@ -202,7 +202,7 @@
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-cyrillic23.woff2') format('woff2');
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 */
@@ -211,7 +211,7 @@
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-greek-ext24.woff2') format('woff2');
src: url('~assets/fonts/Roboto-500-greek-ext24.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@@ -220,7 +220,7 @@
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-greek25.woff2') format('woff2');
src: url('~assets/fonts/Roboto-500-greek25.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@@ -229,7 +229,7 @@
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-vietnamese26.woff2') format('woff2');
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 */
@@ -238,7 +238,7 @@
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-latin-ext27.woff2') format('woff2');
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 */
@@ -247,7 +247,7 @@
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/assets/fonts/Roboto-500-latin28.woff2') format('woff2');
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 */
@@ -256,7 +256,7 @@
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-cyrillic-ext29.woff2') format('woff2');
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 */
@@ -265,7 +265,7 @@
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-cyrillic30.woff2') format('woff2');
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 */
@@ -274,7 +274,7 @@
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-greek-ext31.woff2') format('woff2');
src: url('~assets/fonts/Roboto-700-greek-ext31.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@@ -283,7 +283,7 @@
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-greek32.woff2') format('woff2');
src: url('~assets/fonts/Roboto-700-greek32.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@@ -292,7 +292,7 @@
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-vietnamese33.woff2') format('woff2');
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 */
@@ -301,7 +301,7 @@
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-latin-ext34.woff2') format('woff2');
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 */
@@ -310,7 +310,7 @@
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/assets/fonts/Roboto-700-latin35.woff2') format('woff2');
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 */
@@ -319,7 +319,7 @@
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-cyrillic-ext36.woff2') format('woff2');
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 */
@@ -328,7 +328,7 @@
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-cyrillic37.woff2') format('woff2');
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 */
@@ -337,7 +337,7 @@
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-greek-ext38.woff2') format('woff2');
src: url('~assets/fonts/Roboto-900-greek-ext38.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@@ -346,7 +346,7 @@
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-greek39.woff2') format('woff2');
src: url('~assets/fonts/Roboto-900-greek39.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@@ -355,7 +355,7 @@
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-vietnamese40.woff2') format('woff2');
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 */
@@ -364,7 +364,7 @@
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-latin-ext41.woff2') format('woff2');
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 */
@@ -373,6 +373,6 @@
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/assets/fonts/Roboto-900-latin42.woff2') format('woff2');
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;
}

View File

@@ -0,0 +1,74 @@
<template>
<v-container v-if="book" fluid>
<v-app-bar color="transparent" flat class="mt-n1 rounded">
<v-icon large left> {{ $globals.icons.pages }} </v-icon>
<v-toolbar-title class="headline"> {{ book.name }} </v-toolbar-title>
</v-app-bar>
<v-card flat>
<v-card-text class="py-0">
{{ book.description }}
</v-card-text>
</v-card>
<v-container class="pa-0">
<RecipeCardSection
class="mb-5 mx-1"
:recipes="recipes"
:query="{ cookbook: slug }"
:group-slug="groupSlug"
@sortRecipes="assignSorted"
@replaceRecipes="replaceRecipes"
@appendRecipes="appendRecipes"
@delete="removeRecipe"
/>
</v-container>
</v-container>
</template>
<script lang="ts">
import { computed, defineComponent, useRoute, ref, useContext, useMeta } from "@nuxtjs/composition-api";
import { useLazyRecipes } from "~/composables/recipes";
import RecipeCardSection from "@/components/Domain/Recipe/RecipeCardSection.vue";
import { useCookbook } from "~/composables/use-group-cookbooks";
export default defineComponent({
components: { RecipeCardSection },
props: {
groupSlug: {
type: String,
default: undefined,
}
},
setup(props) {
const { $auth } = useContext();
const loggedIn = computed(() => $auth.loggedIn);
const { recipes, appendRecipes, assignSorted, removeRecipe, replaceRecipes } = useLazyRecipes(loggedIn.value ? null : props.groupSlug);
const route = useRoute();
const slug = route.value.params.slug;
const { getOne } = useCookbook(loggedIn.value ? null : props.groupSlug);
const tab = ref(null);
const book = getOne(slug);
useMeta(() => {
return {
title: book?.value?.name || "Cookbook",
};
});
return {
book,
slug,
tab,
appendRecipes,
assignSorted,
recipes,
removeRecipe,
replaceRecipes,
};
},
head: {}, // Must include for useMeta
});
</script>

View File

@@ -8,9 +8,11 @@
<RecipeOrganizerSelector v-model="inputCategories" selector-type="categories" />
<RecipeOrganizerSelector v-model="inputTags" selector-type="tags" />
<!-- TODO Make this localizable -->
{{ inputDay === "unset" ? "This rule will apply to all days" : `This rule applies on ${inputDay}s` }}
{{ inputEntryType === "unset" ? "for all meal types" : ` and for ${inputEntryType} meal types` }}
<!-- TODO: proper pluralization of inputDay -->
{{ $t('meal-plan.this-rule-will-apply', {
dayCriteria: inputDay === "unset" ? $t('meal-plan.to-all-days') : $t('meal-plan.on-days', [inputDay]),
mealTypeCriteria: inputEntryType === "unset" ? $t('meal-plan.for-all-meal-types') : $t('meal-plan.for-type-meal-types', [inputEntryType])
}) }}
</div>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<div v-if="preferences">
<BaseCardSectionTitle title="General Preferences"></BaseCardSectionTitle>
<v-checkbox v-model="preferences.privateGroup" class="mt-n4" label="Private Group"></v-checkbox>
<BaseCardSectionTitle :title="$tc('group.general-preferences')"></BaseCardSectionTitle>
<v-checkbox v-model="preferences.privateGroup" class="mt-n4" :label="$t('group.private-group')"></v-checkbox>
<v-select
v-model="preferences.firstDayOfWeek"
:prepend-icon="$globals.icons.calendarWeekBegin"
@@ -11,7 +11,7 @@
:label="$t('settings.first-day-of-week')"
/>
<BaseCardSectionTitle class="mt-5" title="Group Recipe Preferences"></BaseCardSectionTitle>
<BaseCardSectionTitle class="mt-5" :title="$tc('group.group-recipe-preferences')"></BaseCardSectionTitle>
<template v-for="(_, key) in preferences">
<v-checkbox
v-if="labels[key]"
@@ -38,12 +38,12 @@ export default defineComponent({
const { i18n } = useContext();
const labels = {
recipePublic: "Allow users outside of your group to see your recipes",
recipeShowNutrition: "Show nutrition information",
recipeShowAssets: "Show recipe assets",
recipeLandscapeView: "Default to landscape view",
recipeDisableComments: "Disable recipe comments from users in your group",
recipeDisableAmount: "Disable organizing recipe ingredients by units and food",
recipePublic: i18n.tc("group.allow-users-outside-of-your-group-to-see-your-recipes"),
recipeShowNutrition: i18n.tc("group.show-nutrition-information"),
recipeShowAssets: i18n.tc("group.show-recipe-assets"),
recipeLandscapeView: i18n.tc("group.default-to-landscape-view"),
recipeDisableComments: i18n.tc("group.disable-users-from-commenting-on-recipes"),
recipeDisableAmount: i18n.tc("group.disable-organizing-recipe-ingredients-by-units-and-food"),
};
const allDays = [
@@ -96,4 +96,4 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
</style>
</style>

View File

@@ -18,6 +18,8 @@
icon: $globals.icons.testTube,
text: $tc('general.test'),
event: 'test',
// TODO: There is no functionality hooked up to this. Enable it when there is
disabled: true,
},
{
icon: $globals.icons.save,

View File

@@ -9,7 +9,7 @@
>
<BaseDialog
v-model="deleteDialog"
:title="$t('recipe.delete-recipe')"
:title="$tc('recipe.delete-recipe')"
color="error"
:icon="$globals.icons.alertCircle"
@confirm="emitDelete()"
@@ -22,54 +22,65 @@
<v-spacer></v-spacer>
<div v-if="!open" class="custom-btn-group ma-1">
<RecipeFavoriteBadge v-if="loggedIn" class="mx-1" color="info" button-style :slug="recipe.slug" show-always />
<v-tooltip v-if="!locked" bottom color="info">
<template #activator="{ on, attrs }">
<v-btn fab small class="mx-1" color="info" v-bind="attrs" v-on="on" @click="$emit('edit', true)">
<v-icon> {{ $globals.icons.edit }} </v-icon>
</v-btn>
</template>
<span>{{ $t("general.edit") }}</span>
</v-tooltip>
<v-tooltip v-else bottom color="info">
<template #activator="{ on, attrs }">
<v-btn fab small class="mx-1" color="info" v-bind="attrs" v-on="on">
<v-icon> {{ $globals.icons.lock }} </v-icon>
</v-btn>
</template>
<span> {{ $t("recipe.locked-by-owner") }} </span>
</v-tooltip>
<RecipeTimelineBadge v-if="loggedIn" button-style :slug="recipe.slug" :recipe-name="recipe.name" />
<div v-if="loggedIn">
<v-tooltip v-if="!locked" bottom color="info">
<template #activator="{ on, attrs }">
<v-btn fab small class="mx-1" color="info" v-bind="attrs" v-on="on" @click="$emit('edit', true)">
<v-icon> {{ $globals.icons.edit }} </v-icon>
</v-btn>
</template>
<span>{{ $t("general.edit") }}</span>
</v-tooltip>
<v-tooltip v-else bottom color="info">
<template #activator="{ on, attrs }">
<v-btn fab small class="mx-1" color="info" v-bind="attrs" v-on="on">
<v-icon> {{ $globals.icons.lock }} </v-icon>
</v-btn>
</template>
<span> {{ $t("recipe.locked-by-owner") }} </span>
</v-tooltip>
</div>
<RecipeTimerMenu
fab
color="info"
class="mr-1"
/>
<RecipeContextMenu
show-print
:menu-top="false"
:name="recipe.name"
:group-id="recipe.groupId"
:slug="recipe.slug"
:menu-icon="$globals.icons.dotsVertical"
fab
color="info"
:card-menu="false"
:recipe="recipe"
:recipe-id="recipe.id"
:recipe-scale="recipeScale"
:use-items="{
delete: false,
edit: false,
download: true,
mealplanner: true,
shoppingList: true,
download: loggedIn,
duplicate: loggedIn,
mealplanner: loggedIn,
shoppingList: loggedIn,
print: true,
share: true,
publicUrl: recipe.settings ? recipe.settings.public : false,
printPreferences: true,
share: loggedIn,
publicUrl: recipe.settings && loggedIn ? recipe.settings.public : false,
}"
@print="$emit('print')"
/>
</div>
<div v-if="open" class="custom-btn-group mb-">
<div v-if="open" class="custom-btn-group gapped">
<v-btn
v-for="(btn, index) in editorButtons"
:key="index"
:fab="$vuetify.breakpoint.xs"
:small="$vuetify.breakpoint.xs"
class="mx-1"
:color="btn.color"
@click="emitHandler(btn.event)"
>
@@ -84,6 +95,8 @@
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
import RecipeContextMenu from "./RecipeContextMenu.vue";
import RecipeFavoriteBadge from "./RecipeFavoriteBadge.vue";
import RecipeTimerMenu from "./RecipeTimerMenu.vue";
import RecipeTimelineBadge from "./RecipeTimelineBadge.vue";
import { Recipe } from "~/lib/api/types/recipe";
const SAVE_EVENT = "save";
@@ -93,7 +106,7 @@ const JSON_EVENT = "json";
const OCR_EVENT = "ocr";
export default defineComponent({
components: { RecipeContextMenu, RecipeFavoriteBadge },
components: { RecipeContextMenu, RecipeFavoriteBadge, RecipeTimerMenu, RecipeTimelineBadge },
props: {
recipe: {
required: true,
@@ -103,6 +116,10 @@ export default defineComponent({
required: true,
type: String,
},
recipeScale: {
type: Number,
default: 1,
},
open: {
required: true,
type: Boolean,
@@ -204,6 +221,10 @@ export default defineComponent({
display: inline-flex;
}
.gapped {
gap: 0.25rem;
}
.vertical {
flex-direction: column !important;
}

View File

@@ -4,7 +4,7 @@
<v-card
:class="{ 'on-hover': hover }"
:elevation="hover ? 12 : 2"
:to="route ? `/recipe/${slug}` : ''"
:to="route ? recipeRoute : ''"
:min-height="imageHeight + 75"
@click="$emit('click')"
>
@@ -19,7 +19,9 @@
<v-expand-transition v-if="description">
<div v-if="hover" class="d-flex transition-fast-in-fast-out secondary v-card--reveal" style="height: 100%">
<v-card-text class="v-card--text-show white--text">
{{ description }}
<div class="descriptionWrapper">
<SafeMarkdown :source="description" />
</div>
</v-card-text>
</div>
</v-expand-transition>
@@ -37,7 +39,10 @@
<RecipeRating class="pb-1" :value="rating" :name="name" :slug="slug" :small="true" />
<v-spacer></v-spacer>
<RecipeChips :truncate="true" :items="tags" :title="false" :limit="2" :small="true" url-prefix="tags" />
<!-- If we're not logged-in, no items display, so we hide this menu -->
<RecipeContextMenu
v-if="loggedIn"
color="grey darken-2"
:slug="slug"
:name="name"
@@ -49,6 +54,7 @@
mealplanner: true,
shoppingList: true,
print: false,
printPreferences: false,
share: true,
publicUrl: false,
}"
@@ -77,6 +83,10 @@ export default defineComponent({
type: String,
required: true,
},
groupSlug: {
type: String,
default: null,
},
slug: {
type: String,
required: true,
@@ -112,14 +122,19 @@ export default defineComponent({
default: 200,
},
},
setup() {
setup(props) {
const { $auth } = useContext();
const loggedIn = computed(() => {
return $auth.loggedIn;
});
const recipeRoute = computed<string>(() => {
return loggedIn.value ? `/recipe/${props.slug}` : `/explore/recipes/${props.groupSlug}/${props.slug}`;
});
return {
loggedIn,
recipeRoute,
};
},
});
@@ -143,4 +158,10 @@ export default defineComponent({
overflow: hidden;
text-overflow: ellipsis;
}
.descriptionWrapper{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 8;
overflow: hidden;
}
</style>

View File

@@ -2,14 +2,24 @@
<v-expand-transition>
<v-card
:ripple="false"
class="mx-auto"
:class="isFlat ? 'mx-auto flat' : 'mx-auto'"
hover
:to="$listeners.selected ? undefined : `/recipe/${slug}`"
:to="$listeners.selected ? undefined : recipeRoute"
@click="$emit('selected')"
>
<v-list-item three-line>
<slot name="avatar">
<v-list-item-avatar tile size="125" class="v-mobile-img rounded-sm my-0 ml-n4">
<v-img v-if="vertical" class="rounded-sm">
<RecipeCardImage
:icon-size="100"
:height="150"
:slug="slug"
:recipe-id="recipeId"
small
:image-version="image"
/>
</v-img>
<v-list-item three-line :class="vertical ? 'px-2' : 'px-0'">
<slot v-if="!vertical" name="avatar">
<v-list-item-avatar tile size="125" class="v-mobile-img rounded-sm my-0">
<RecipeCardImage
:icon-size="100"
:height="125"
@@ -17,18 +27,20 @@
:recipe-id="recipeId"
small
:image-version="image"
></RecipeCardImage>
/>
</v-list-item-avatar>
</slot>
<v-list-item-content>
<v-list-item-title class="mb-1">{{ name }} </v-list-item-title>
<v-list-item-subtitle> {{ description }} </v-list-item-subtitle>
<div class="d-flex justify-center align-center">
<v-list-item-content class="py-0">
<v-list-item-title class="mt-3 mb-1">{{ name }} </v-list-item-title>
<v-list-item-subtitle>
<SafeMarkdown :source="description" />
</v-list-item-subtitle>
<div class="d-flex flex-wrap justify-end align-center">
<slot name="actions">
<RecipeFavoriteBadge v-if="loggedIn" :slug="slug" show-always />
<v-rating
color="secondary"
class="ml-auto"
:class="loggedIn ? 'ml-auto' : 'ml-auto pb-2'"
background-color="secondary lighten-3"
dense
length="5"
@@ -36,7 +48,11 @@
:value="rating"
></v-rating>
<v-spacer></v-spacer>
<!-- If we're not logged-in, no items display, so we hide this menu -->
<!-- We also add padding to the v-rating above to compensate -->
<RecipeContextMenu
v-if="loggedIn"
:slug="slug"
:menu-icon="$globals.icons.dotsHorizontal"
:name="name"
@@ -48,6 +64,7 @@
mealplanner: true,
shoppingList: true,
print: false,
printPreferences: false,
share: true,
publicUrl: false,
}"
@@ -79,6 +96,10 @@ export default defineComponent({
type: String,
required: true,
},
groupSlug: {
type: String,
default: null,
},
slug: {
type: String,
required: true,
@@ -104,15 +125,28 @@ export default defineComponent({
type: String,
required: true,
},
vertical: {
type: Boolean,
default: false,
},
isFlat: {
type: Boolean,
default: false,
},
},
setup() {
setup(props) {
const { $auth } = useContext();
const loggedIn = computed(() => {
return $auth.loggedIn;
});
const recipeRoute = computed<string>(() => {
return loggedIn.value ? `/recipe/${props.slug}` : `/explore/recipes/${props.groupSlug}/${props.slug}`;
});
return {
loggedIn,
recipeRoute,
};
},
});
@@ -145,4 +179,9 @@ export default defineComponent({
.text-top {
align-self: start !important;
}
.flat, .theme--dark .flat {
box-shadow: none!important;
background-color: transparent!important;
}
</style>

View File

@@ -49,13 +49,19 @@
</v-icon>
<v-list-item-title>{{ $t("general.updated") }}</v-list-item-title>
</v-list-item>
<v-list-item @click="sortRecipes(EVENTS.lastMade)">
<v-icon left>
{{ $globals.icons.chefHat }}
</v-icon>
<v-list-item-title>{{ $t("general.last-made") }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<ContextMenu
v-if="!$vuetify.breakpoint.xsOnly"
:items="[
{
title: $t('general.toggle-view'),
title: $tc('general.toggle-view'),
icon: $globals.icons.eye,
event: 'toggle-dense-view',
},
@@ -70,12 +76,12 @@
<RecipeCard
:name="recipe.name"
:description="recipe.description"
:group-slug="groupSlug"
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:tags="recipe.tags"
:recipe-id="recipe.id"
@delete="$emit('delete', recipe.slug)"
/>
</v-lazy>
</v-col>
@@ -94,12 +100,12 @@
<RecipeCardMobile
:name="recipe.name"
:description="recipe.description"
:group-slug="groupSlug"
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:tags="recipe.tags"
:recipe-id="recipe.id"
@delete="$emit('delete', recipe.slug)"
/>
</v-lazy>
</v-col>
@@ -123,6 +129,7 @@ import {
useAsync,
useContext,
useRouter,
watch,
} from "@nuxtjs/composition-api";
import { useThrottleFn } from "@vueuse/core";
import RecipeCard from "./RecipeCard.vue";
@@ -131,6 +138,7 @@ import { useAsyncKey } from "~/composables/use-utils";
import { useLazyRecipes } from "~/composables/recipes";
import { Recipe } from "~/lib/api/types/recipe";
import { useUserSortPreferences } from "~/composables/use-users/preferences";
import { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
const REPLACE_RECIPES_EVENT = "replaceRecipes";
const APPEND_RECIPES_EVENT = "appendRecipes";
@@ -157,24 +165,16 @@ export default defineComponent({
type: Boolean,
default: false,
},
groupSlug: {
type: String,
default: null,
},
recipes: {
type: Array as () => Recipe[],
default: () => [],
},
cookbookSlug: {
type: String,
default: null,
},
categorySlug: {
type: String,
default: null,
},
tagSlug: {
type: String,
default: null,
},
toolSlug: {
type: String,
query: {
type: Object as () => RecipeSearchQuery,
default: null,
},
},
@@ -186,10 +186,14 @@ export default defineComponent({
rating: "rating",
created: "created",
updated: "updated",
lastMade: "lastMade",
shuffle: "shuffle",
};
const { $globals, $vuetify } = useContext();
const { $auth, $globals, $vuetify } = useContext();
const loggedIn = computed(() => {
return $auth.loggedIn;
});
const useMobileCards = computed(() => {
return $vuetify.breakpoint.smAndDown || preferences.value.useMobileCards;
});
@@ -207,44 +211,69 @@ export default defineComponent({
if (props.recipes.length > 0) {
const recipe = props.recipes[Math.floor(Math.random() * props.recipes.length)];
if (recipe.slug !== undefined) {
router.push(`/recipe/${recipe.slug}`);
router.push(loggedIn.value ? `/recipe/${recipe.slug}` : `/explore/recipes/${props.groupSlug}/${recipe.slug}`);
}
}
}
const page = ref(1);
const perPage = ref(32);
const perPage = 32;
const hasMore = ref(true);
const ready = ref(false);
const loading = ref(false);
const cookbook = ref<string>(props.cookbookSlug);
const category = ref<string>(props.categorySlug);
const tag = ref<string>(props.tagSlug);
const tool = ref<string>(props.toolSlug);
const { fetchMore } = useLazyRecipes(loggedIn.value ? null : props.groupSlug);
const { fetchMore } = useLazyRecipes();
const queryFilter = computed(() => {
const orderBy = props.query?.orderBy || preferences.value.orderBy;
return preferences.value.filterNull && orderBy ? `${orderBy} IS NOT NULL` : null;
});
async function fetchRecipes(pageCount = 1) {
return await fetchMore(
page.value,
// we double-up the first call to avoid a bug with large screens that render the entire first page without scrolling, preventing additional loading
perPage * pageCount,
props.query?.orderBy || preferences.value.orderBy,
props.query?.orderDirection || preferences.value.orderDirection,
props.query,
// filter out recipes that have a null value for the property we're sorting by
queryFilter.value
);
}
onMounted(async () => {
const newRecipes = await fetchMore(
page.value,
if (props.query) {
await initRecipes();
ready.value = true;
}
});
// we double-up the first call to avoid a bug with large screens that render the entire first page without scrolling, preventing additional loading
perPage.value * 2,
preferences.value.orderBy,
preferences.value.orderDirection,
cookbook.value,
category.value,
tag.value,
tool.value
);
let lastQuery: string | undefined;
watch(
() => props.query,
async (newValue: RecipeSearchQuery | undefined) => {
const newValueString = JSON.stringify(newValue)
if (newValue && (!ready.value || lastQuery !== newValueString)) {
lastQuery = newValueString;
await initRecipes();
ready.value = true;
}
}
);
async function initRecipes() {
page.value = 1;
const newRecipes = await fetchRecipes(2);
if (!newRecipes.length) {
hasMore.value = false;
}
// since we doubled the first call, we also need to advance the page
page.value = page.value + 1;
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
ready.value = true;
});
}
const infiniteScroll = useThrottleFn(() => {
useAsync(async () => {
@@ -255,16 +284,7 @@ export default defineComponent({
loading.value = true;
page.value = page.value + 1;
const newRecipes = await fetchMore(
page.value,
perPage.value,
preferences.value.orderBy,
preferences.value.orderDirection,
cookbook.value,
category.value,
tag.value,
tool.value
);
const newRecipes = await fetchRecipes();
if (!newRecipes.length) {
hasMore.value = false;
} else {
@@ -280,10 +300,17 @@ export default defineComponent({
return;
}
function setter(orderBy: string, ascIcon: string, descIcon: string) {
function setter(
orderBy: string,
ascIcon: string,
descIcon: string,
defaultOrderDirection = "asc",
filterNull = false
) {
if (preferences.value.orderBy !== orderBy) {
preferences.value.orderBy = orderBy;
preferences.value.orderDirection = "asc";
preferences.value.orderDirection = defaultOrderDirection;
preferences.value.filterNull = filterNull;
} else {
preferences.value.orderDirection = preferences.value.orderDirection === "asc" ? "desc" : "asc";
}
@@ -292,16 +319,37 @@ export default defineComponent({
switch (sortType) {
case EVENTS.az:
setter("name", $globals.icons.sortAlphabeticalAscending, $globals.icons.sortAlphabeticalDescending);
setter(
"name",
$globals.icons.sortAlphabeticalAscending,
$globals.icons.sortAlphabeticalDescending,
"asc",
false
);
break;
case EVENTS.rating:
setter("rating", $globals.icons.sortAscending, $globals.icons.sortDescending);
setter("rating", $globals.icons.sortAscending, $globals.icons.sortDescending, "desc", true);
break;
case EVENTS.created:
setter("created_at", $globals.icons.sortCalendarAscending, $globals.icons.sortCalendarDescending);
setter(
"created_at",
$globals.icons.sortCalendarAscending,
$globals.icons.sortCalendarDescending,
"desc",
false
);
break;
case EVENTS.updated:
setter("update_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending);
setter("update_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending, "desc", false);
break;
case EVENTS.lastMade:
setter(
"last_made",
$globals.icons.sortCalendarAscending,
$globals.icons.sortCalendarDescending,
"desc",
true
);
break;
default:
console.log("Unknown Event", sortType);
@@ -317,16 +365,7 @@ export default defineComponent({
loading.value = true;
// fetch new recipes
const newRecipes = await fetchMore(
page.value,
perPage.value,
preferences.value.orderBy,
preferences.value.orderDirection,
cookbook.value,
category.value,
tag.value,
tool.value
);
const newRecipes = await fetchRecipes();
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
state.sortLoading = false;

View File

@@ -9,7 +9,7 @@
color="accent"
:small="small"
dark
:to="`/recipes/${urlPrefix}/${category.slug}`"
:to=" loggedIn ? `/?${urlPrefix}=${category.id}` : undefined"
>
{{ truncateText(category.name) }}
</v-chip>
@@ -17,7 +17,7 @@
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
import { RecipeCategory, RecipeTag, RecipeTool } from "~/lib/api/types/user";
export type UrlPrefixParam = "tags" | "categories" | "tools";
@@ -54,6 +54,11 @@ export default defineComponent({
},
},
setup(props) {
const { $auth } = useContext();
const loggedIn = computed(() => {
return $auth.loggedIn
})
function truncateText(text: string, length = 20, clamp = "...") {
if (!props.truncate) return text;
const node = document.createElement("div");
@@ -63,6 +68,7 @@ export default defineComponent({
}
return {
loggedIn,
truncateText,
};
},

View File

@@ -2,6 +2,7 @@
<div class="text-center">
<!-- Recipe Share Dialog -->
<RecipeDialogShare v-model="shareDialog" :recipe-id="recipeId" :name="name" />
<RecipeDialogPrintPreferences v-model="printPreferencesDialog" :recipe="recipeRef" />
<BaseDialog
v-model="recipeDeleteDialog"
:title="$t('recipe.delete-recipe')"
@@ -13,6 +14,23 @@
{{ $t("recipe.delete-confirmation") }}
</v-card-text>
</BaseDialog>
<BaseDialog
v-model="recipeDuplicateDialog"
:title="$t('recipe.duplicate')"
color="primary"
:icon="$globals.icons.duplicate"
@confirm="duplicateRecipe()"
>
<v-card-text>
<v-text-field
v-model="recipeName"
dense
:label="$t('recipe.recipe-name')"
autofocus
@keyup.enter="duplicateRecipe()"
></v-text-field>
</v-card-text>
</BaseDialog>
<BaseDialog
v-model="mealplannerDialog"
:title="$t('recipe.add-recipe-to-mealplan')"
@@ -58,7 +76,7 @@
:key="list.id"
hover
class="my-2 left-border"
@click="addRecipeToList(list.id)"
@click="openShoppingListIngredientDialog(list)"
>
<v-card-title class="py-2">
{{ list.name }}
@@ -66,6 +84,62 @@
</v-card>
</v-card-text>
</BaseDialog>
<BaseDialog
v-model="shoppingListIngredientDialog"
:title="selectedShoppingList ? selectedShoppingList.name : $t('recipe.add-to-list')"
:icon="$globals.icons.cartCheck"
width="70%"
:submit-text="$tc('recipe.add-to-list')"
@submit="addRecipeToList()"
>
<v-card
elevation="0"
height="fit-content"
max-height="60vh"
width="100%"
:class="$vuetify.breakpoint.smAndDown ? '' : 'ingredient-grid'"
:style="$vuetify.breakpoint.smAndDown ? '' : { gridTemplateRows: `repeat(${Math.ceil(recipeIngredients.length / 2)}, min-content)` }"
style="overflow-y: auto"
>
<v-list-item
v-for="(ingredientData, i) in recipeIngredients"
:key="'ingredient' + i"
dense
@click="recipeIngredients[i].checked = !recipeIngredients[i].checked"
>
<v-checkbox
hide-details
:input-value="ingredientData.checked"
class="pt-0 my-auto py-auto"
color="secondary"
/>
<v-list-item-content :key="ingredientData.ingredient.quantity">
<RecipeIngredientListItem
:ingredient="ingredientData.ingredient"
:disable-amount="ingredientData.disableAmount"
:scale="recipeScale" />
</v-list-item-content>
</v-list-item>
</v-card>
<div class="d-flex justify-end mb-4 mt-2">
<BaseButtonGroup
:buttons="[
{
icon: $globals.icons.checkboxBlankOutline,
text: $tc('shopping-list.uncheck-all-items'),
event: 'uncheck',
},
{
icon: $globals.icons.checkboxOutline,
text: $tc('shopping-list.check-all-items'),
event: 'check',
},
]"
@uncheck="bulkCheckIngredients(false)"
@check="bulkCheckIngredients(true)"
/>
</div>
</BaseDialog>
<v-menu
offset-y
left
@@ -75,7 +149,7 @@
:nudge-top="menuTop ? '5' : '0'"
allow-overflow
close-delay="125"
open-on-hover
:open-on-hover="$vuetify.breakpoint.mdAndUp"
content-class="d-print-none"
>
<template #activator="{ on, attrs }">
@@ -96,11 +170,14 @@
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, useContext, useRouter, ref } from "@nuxtjs/composition-api";
import { computed, defineComponent, reactive, toRefs, useContext, useRouter, ref } from "@nuxtjs/composition-api";
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue";
import RecipeDialogPrintPreferences from "./RecipeDialogPrintPreferences.vue";
import RecipeDialogShare from "./RecipeDialogShare.vue";
import { useUserApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import { planTypeOptions } from "~/composables/use-group-mealplan";
import { usePlanTypeOptions } from "~/composables/use-group-mealplan";
import { Recipe, RecipeIngredient } from "~/lib/api/types/recipe";
import { ShoppingListSummary } from "~/lib/api/types/group";
import { PlanEntryType } from "~/lib/api/types/meal-plan";
import { useAxiosDownloader } from "~/composables/api/use-axios-download";
@@ -113,6 +190,7 @@ export interface ContextMenuIncludes {
mealplanner: boolean;
shoppingList: boolean;
print: boolean;
printPreferences: boolean;
share: boolean;
publicUrl: boolean;
}
@@ -122,12 +200,15 @@ export interface ContextMenuItem {
icon: string;
color: string | undefined;
event: string;
isPublic: boolean;
}
export default defineComponent({
components: {
RecipeDialogPrintPreferences,
RecipeDialogShare,
},
RecipeIngredientListItem
},
props: {
useItems: {
type: Object as () => ContextMenuIncludes,
@@ -135,9 +216,11 @@ export default defineComponent({
delete: true,
edit: true,
download: true,
duplicate: false,
mealplanner: true,
shoppingList: true,
print: true,
printPreferences: true,
share: true,
publicUrl: false,
}),
@@ -176,28 +259,31 @@ export default defineComponent({
required: true,
type: String,
},
recipe: {
type: Object as () => Recipe,
default: undefined,
},
recipeId: {
required: true,
type: String,
},
/**
* Optional group ID prop that is only _required_ when the
* public URL is requested. If the public URL button is pressed
* and the groupId is not set, an error will be thrown.
*/
groupId: {
type: String,
default: "",
recipeScale: {
type: Number,
default: 1,
},
},
setup(props, context) {
const api = useUserApi();
const state = reactive({
printPreferencesDialog: false,
shareDialog: false,
recipeDeleteDialog: false,
mealplannerDialog: false,
shoppingListDialog: false,
shoppingListIngredientDialog: false,
recipeDuplicateDialog: false,
recipeName: props.name,
loading: false,
menuItems: [] as ContextMenuItem[],
newMealdate: "",
@@ -205,7 +291,10 @@ export default defineComponent({
pickerMenu: false,
});
const { i18n, $globals } = useContext();
const { $auth, i18n, $globals } = useContext();
const loggedIn = computed(() => {
return $auth.loggedIn;
});
// ===========================================================================
// Context Menu Setup
@@ -216,48 +305,70 @@ export default defineComponent({
icon: $globals.icons.edit,
color: undefined,
event: "edit",
isPublic: false,
},
delete: {
title: i18n.tc("general.delete"),
icon: $globals.icons.delete,
color: "error",
event: "delete",
isPublic: false,
},
download: {
title: i18n.tc("general.download"),
icon: $globals.icons.download,
color: undefined,
event: "download",
isPublic: false,
},
duplicate: {
title: i18n.tc("general.duplicate"),
icon: $globals.icons.duplicate,
color: undefined,
event: "duplicate",
isPublic: false,
},
mealplanner: {
title: i18n.tc("recipe.add-to-plan"),
icon: $globals.icons.calendar,
color: undefined,
event: "mealplanner",
isPublic: false,
},
shoppingList: {
title: i18n.tc("recipe.add-to-list"),
icon: $globals.icons.cartCheck,
color: undefined,
event: "shoppingList",
isPublic: false,
},
print: {
title: i18n.tc("general.print"),
icon: $globals.icons.printer,
color: undefined,
event: "print",
isPublic: true,
},
printPreferences: {
title: i18n.tc("general.print-preferences"),
icon: $globals.icons.printerSettings,
color: undefined,
event: "printPreferences",
isPublic: true,
},
share: {
title: i18n.tc("general.share"),
icon: $globals.icons.shareVariant,
color: undefined,
event: "share",
isPublic: false,
},
publicUrl: {
title: i18n.tc("recipe.public-link"),
icon: $globals.icons.contentCopy,
color: undefined,
event: "publicUrl",
isPublic: true,
},
};
@@ -265,7 +376,7 @@ export default defineComponent({
for (const [key, value] of Object.entries(props.useItems)) {
if (value) {
const item = defaultItems[key];
if (item) {
if (item && (item.isPublic || loggedIn.value)) {
state.menuItems.push(item);
}
}
@@ -280,6 +391,9 @@ export default defineComponent({
// Context Menu Event Handler
const shoppingLists = ref<ShoppingListSummary[]>();
const selectedShoppingList = ref<ShoppingListSummary>();
const recipeRef = ref<Recipe>(props.recipe);
const recipeIngredients = ref<{ checked: boolean; ingredient: RecipeIngredient, disableAmount: boolean }[]>([]);
async function getShoppingLists() {
const { data } = await api.shopping.lists.getAll();
@@ -288,11 +402,65 @@ export default defineComponent({
}
}
async function addRecipeToList(listId: string) {
const { data } = await api.shopping.lists.addRecipe(listId, props.recipeId);
async function refreshRecipe() {
const { data } = await api.recipes.getOne(props.slug);
if (data) {
recipeRef.value = data;
}
}
async function openShoppingListIngredientDialog(list: ShoppingListSummary) {
selectedShoppingList.value = list;
if (!recipeRef.value) {
await refreshRecipe();
}
if (recipeRef.value?.recipeIngredient) {
recipeIngredients.value = recipeRef.value.recipeIngredient.map((ingredient) => {
return {
checked: true,
ingredient,
disableAmount: recipeRef.value.settings?.disableAmount || false
};
});
}
state.shoppingListDialog = false;
state.shoppingListIngredientDialog = true;
}
function bulkCheckIngredients(value = true) {
recipeIngredients.value.forEach((data) => {
data.checked = value;
});
}
async function addRecipeToList() {
if (!selectedShoppingList.value) {
return;
}
const ingredients: RecipeIngredient[] = [];
recipeIngredients.value.forEach((data) => {
if (data.checked) {
ingredients.push(data.ingredient);
}
});
if (!ingredients.length) {
return;
}
const { data } = await api.shopping.lists.addRecipe(
selectedShoppingList.value.id,
props.recipeId,
props.recipeScale,
ingredients
);
if (data) {
alert.success(i18n.t("recipe.recipe-added-to-list") as string);
state.shoppingListDialog = false;
state.shoppingListIngredientDialog = false;
}
}
@@ -329,7 +497,29 @@ export default defineComponent({
}
}
async function duplicateRecipe() {
const { data } = await api.recipes.duplicateOne(props.slug, state.recipeName);
if (data && data.slug) {
router.push(`/recipe/${data.slug}`);
}
}
const { copyText } = useCopy();
const groupSlug = ref<string>("");
async function setGroupSlug() {
if (groupSlug.value) {
return;
}
const { data } = await api.users.getSelfGroup();
if (data) {
groupSlug.value = data.slug;
} else {
// @ts-ignore this will either be a string or undefined
groupSlug.value = $auth.user?.groupId
}
}
// Note: Print is handled as an event in the parent component
const eventHandlers: { [key: string]: () => void | Promise<any> } = {
@@ -338,23 +528,34 @@ export default defineComponent({
},
edit: () => router.push(`/recipe/${props.slug}` + "?edit=true"),
download: handleDownloadEvent,
duplicate: () => {
state.recipeDuplicateDialog = true;
},
mealplanner: () => {
state.mealplannerDialog = true;
},
printPreferences: async () => {
if (!recipeRef.value) {
await refreshRecipe();
}
state.printPreferencesDialog = true;
},
shoppingList: () => {
getShoppingLists();
state.shoppingListDialog = true;
state.shoppingListIngredientDialog = false;
},
share: () => {
state.shareDialog = true;
},
publicUrl: () => {
if (!props.groupId) {
alert.error("Unknown group ID");
console.error("prop `groupId` is required when requesting a public URL");
publicUrl: async () => {
await setGroupSlug();
if (!groupSlug.value) {
return;
}
copyText(`${window.location.origin}/explore/recipes/${props.groupId}/${props.slug}`);
copyText(`${window.location.origin}/explore/recipes/${groupSlug.value}/${props.slug}`);
},
};
@@ -371,16 +572,33 @@ export default defineComponent({
state.loading = false;
}
const planTypeOptions = usePlanTypeOptions();
return {
...toRefs(state),
recipeRef,
shoppingLists,
selectedShoppingList,
openShoppingListIngredientDialog,
addRecipeToList,
bulkCheckIngredients,
duplicateRecipe,
contextMenuEventHandler,
deleteRecipe,
addRecipeToPlan,
icon,
planTypeOptions,
recipeIngredients,
};
},
});
</script>
<style scoped lang="css">
.ingredient-grid {
display: grid;
grid-auto-flow: column;
grid-template-columns: 1fr 1fr;
grid-gap: 0.5rem;
}
</style>

View File

@@ -17,6 +17,9 @@
<td colspan="4"></td>
</tr>
</template>
<template #item.name="{ item }">
<a :href="`/recipe/${item.slug}`" style="color: inherit; text-decoration: inherit; " @click="$emit('click')">{{ item.name }}</a>
</template>
<template #item.tags="{ item }">
<RecipeChip small :items="item.tags" :is-category="false" url-prefix="tags" />
</template>
@@ -28,9 +31,7 @@
</template>
<template #item.userId="{ item }">
<v-list-item class="justify-start">
<v-list-item-avatar>
<img src="https://i.pravatar.cc/300" alt="John" />
</v-list-item-avatar>
<UserAvatar :user-id="item.userId" size="40" />
<v-list-item-content>
<v-list-item-title>
{{ getMember(item.userId) }}
@@ -43,6 +44,7 @@
<script lang="ts">
import { computed, defineComponent, onMounted, ref, useContext } from "@nuxtjs/composition-api";
import UserAvatar from "../User/UserAvatar.vue";
import RecipeChip from "./RecipeChips.vue";
import { Recipe } from "~/lib/api/types/recipe";
import { useUserApi } from "~/composables/api";
@@ -61,7 +63,7 @@ interface ShowHeaders {
}
export default defineComponent({
components: { RecipeChip },
components: { RecipeChip, UserAvatar },
props: {
value: {
type: Array,

View File

@@ -0,0 +1,89 @@
<template>
<BaseDialog
v-model="dialog"
:icon="$globals.icons.printerSettings"
:title="$tc('general.print-preferences')"
width="70%"
max-width="816px"
>
<div class="pa-6">
<v-container class="print-config mb-3 pa-0">
<v-row>
<v-col cols="auto" align-self="center" class="text-center">
<div class="text-subtitle-2" style="text-align: center;">{{ $tc('recipe.recipe-image') }}</div>
<v-btn-toggle v-model="preferences.imagePosition" mandatory style="width: fit-content;">
<v-btn :value="ImagePosition.left">
<v-icon>{{ $globals.icons.dockLeft }}</v-icon>
</v-btn>
<v-btn :value="ImagePosition.right">
<v-icon>{{ $globals.icons.dockRight }}</v-icon>
</v-btn>
<v-btn :value="ImagePosition.hidden">
<v-icon>{{ $globals.icons.windowClose }}</v-icon>
</v-btn>
</v-btn-toggle>
</v-col>
<v-col cols="auto" align-self="start">
<v-row no-gutters>
<v-switch v-model="preferences.showDescription" hide-details :label="$tc('recipe.description')" />
</v-row>
<v-row no-gutters>
<v-switch v-model="preferences.showNotes" hide-details :label="$tc('recipe.notes')" />
</v-row>
</v-col>
</v-row>
</v-container>
<v-card
height="fit-content"
max-height="40vh"
width="100%"
class="print-preview"
style="overflow-y: auto;"
>
<RecipePrintView :recipe="recipe"/>
</v-card>
</div>
</BaseDialog>
</template>
<script lang="ts">
import { computed, defineComponent } from "@nuxtjs/composition-api";
import { Recipe } from "~/lib/api/types/recipe";
import { ImagePosition, useUserPrintPreferences } from "~/composables/use-users/preferences";
import RecipePrintView from "~/components/Domain/Recipe/RecipePrintView.vue";
export default defineComponent({
components: {
RecipePrintView,
},
props: {
value: {
type: Boolean,
default: false,
},
recipe: {
type: Object as () => Recipe,
default: undefined,
},
},
setup(props, context) {
const preferences = useUserPrintPreferences();
// V-Model Support
const dialog = computed({
get: () => {
return props.value;
},
set: (val) => {
context.emit("input", val);
},
});
return {
dialog,
ImagePosition,
preferences,
}
}
});
</script>

View File

@@ -5,7 +5,7 @@
<v-app-bar sticky dark color="primary lighten-1" :rounded="!$vuetify.breakpoint.xs">
<v-text-field
id="arrow-search"
v-model="search"
v-model="search.query.value"
autofocus
solo
flat
@@ -31,11 +31,11 @@
<div class="mr-auto">
{{ $t("search.results") }}
</div>
<router-link to="/search?advanced=true"> {{ $t("search.advanced-search") }} </router-link>
<router-link to="/"> {{ $t("search.advanced-search") }} </router-link>
</v-card-actions>
<RecipeCardMobile
v-for="(recipe, index) in results.slice(0, 10)"
v-for="(recipe, index) in search.data.value"
:key="index"
:tabindex="index"
class="ma-1 arrow-nav"
@@ -56,8 +56,9 @@
<script lang="ts">
import { defineComponent, toRefs, reactive, ref, watch, useRoute } from "@nuxtjs/composition-api";
import RecipeCardMobile from "./RecipeCardMobile.vue";
import { useRecipes, allRecipes, useRecipeSearch } from "~/composables/recipes";
import { RecipeSummary } from "~/lib/api/types/recipe";
import { useUserApi } from "~/composables/api";
import { useRecipeSearch } from "~/composables/recipes/use-recipe-search";
const SELECTED_EVENT = "selected";
export default defineComponent({
components: {
@@ -65,12 +66,9 @@ export default defineComponent({
},
setup(_, context) {
const { refreshRecipes } = useRecipes(true, false);
const state = reactive({
loading: false,
selectedIndex: -1,
searchResults: [],
});
// ===========================================================================
@@ -78,14 +76,11 @@ export default defineComponent({
const dialog = ref(false);
// Reset or Grab Recipes on Change
watch(dialog, async (val) => {
watch(dialog, (val) => {
if (!val) {
search.value = "";
search.query.value = "";
state.selectedIndex = -1;
} else if (allRecipes.value && allRecipes.value.length <= 0) {
state.loading = true;
await refreshRecipes();
state.loading = false;
search.data.value = [];
}
});
@@ -145,9 +140,9 @@ export default defineComponent({
// ===========================================================================
// Basic Search
const api = useUserApi();
const search = useRecipeSearch(api);
const { search, results } = useRecipeSearch(allRecipes);
// ===========================================================================
// Select Handler
function handleSelect(recipe: RecipeSummary) {
@@ -155,13 +150,20 @@ export default defineComponent({
context.emit(SELECTED_EVENT, recipe);
}
return { allRecipes, refreshRecipes, ...toRefs(state), dialog, open, close, handleSelect, search, results };
return {
...toRefs(state),
dialog,
open,
close,
handleSelect,
search,
};
},
});
</script>
<style>
.scroll {
overflow-y: scroll;
overflow-y: auto;
}
</style>

View File

@@ -84,7 +84,6 @@ export default defineComponent({
return props.value;
},
set: (val) => {
console.log(val);
context.emit("input", val);
},
});

View File

@@ -0,0 +1,495 @@
<template>
<v-container fluid class="pa-0">
<div class="search-container py-8">
<form class="search-box pa-2" @submit.prevent="search">
<div class="d-flex justify-center my-2">
<v-text-field
ref="input"
v-model="state.search"
outlined
hide-details
clearable
color="primary"
:placeholder="$tc('search.search-placeholder')"
:prepend-inner-icon="$globals.icons.search"
@keyup.enter="hideKeyboard"
/>
</div>
<div class="search-row">
<!-- Category Filter -->
<SearchFilter
v-if="categories"
v-model="selectedCategories"
:require-all.sync="state.requireAllCategories"
:items="categories"
>
<v-icon left>
{{ $globals.icons.tags }}
</v-icon>
{{ $t("category.categories") }}
</SearchFilter>
<!-- Tag Filter -->
<SearchFilter v-if="tags" v-model="selectedTags" :require-all.sync="state.requireAllTags" :items="tags">
<v-icon left>
{{ $globals.icons.tags }}
</v-icon>
{{ $t("tag.tags") }}
</SearchFilter>
<!-- Tool Filter -->
<SearchFilter v-if="tools" v-model="selectedTools" :require-all.sync="state.requireAllTools" :items="tools">
<v-icon left>
{{ $globals.icons.tools }}
</v-icon>
{{ $t("tool.tools") }}
</SearchFilter>
<!-- Food Filter -->
<SearchFilter v-if="foods" v-model="selectedFoods" :require-all.sync="state.requireAllFoods" :items="foods">
<v-icon left>
{{ $globals.icons.foods }}
</v-icon>
{{ $t("general.foods") }}
</SearchFilter>
<!-- Sort Options -->
<v-menu offset-y nudge-bottom="3">
<template #activator="{ on, attrs }">
<v-btn class="ml-auto" small color="accent" v-bind="attrs" v-on="on">
<v-icon :left="!$vuetify.breakpoint.xsOnly">
{{ state.orderDirection === "asc" ? $globals.icons.sortAscending : $globals.icons.sortDescending }}
</v-icon>
{{ $vuetify.breakpoint.xsOnly ? null : sortText }}
</v-btn>
</template>
<v-card>
<v-list>
<v-list-item @click="toggleOrderDirection()">
<v-icon left>
{{ $globals.icons.sort }}
</v-icon>
<v-list-item-title>
{{ state.orderDirection === "asc" ? "Sort Descending" : "Sort Ascending" }}
</v-list-item-title>
</v-list-item>
<v-list-item
v-for="v in sortable"
:key="v.name"
:input-value="state.orderBy === v.value"
@click="state.orderBy = v.value"
>
<v-icon left>
{{ v.icon }}
</v-icon>
<v-list-item-title>{{ v.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-card>
</v-menu>
<!-- Settings -->
<v-menu offset-y bottom left nudge-bottom="3" :close-on-content-click="false">
<template #activator="{ on, attrs }">
<v-btn small color="accent" dark v-bind="attrs" v-on="on">
<v-icon small>
{{ $globals.icons.cog }}
</v-icon>
</v-btn>
</template>
<v-card>
<v-card-text>
<v-switch v-model="state.auto" :label="$t('search.auto-search')" single-line></v-switch>
<v-btn block color="primary" @click="reset">
{{ $tc("general.reset") }}
</v-btn>
</v-card-text>
</v-card>
</v-menu>
</div>
<div v-if="!state.auto" class="search-button-container">
<v-btn x-large color="primary" type="submit" block>
<v-icon left>
{{ $globals.icons.search }}
</v-icon>
{{ $tc("search.search") }}
</v-btn>
</div>
</form>
</div>
<v-divider></v-divider>
<v-container class="mt-6 px-md-6">
<RecipeCardSection
class="mt-n5"
:icon="$globals.icons.search"
:title="$tc('search.results')"
:group-slug="groupSlug"
:recipes="recipes"
:query="passedQuery"
@replaceRecipes="replaceRecipes"
@appendRecipes="appendRecipes"
/>
</v-container>
</v-container>
</template>
<script lang="ts">
import { ref, defineComponent, useRouter, onMounted, useContext, computed, Ref } from "@nuxtjs/composition-api";
import { watchDebounced } from "@vueuse/shared";
import SearchFilter from "~/components/Domain/SearchFilter.vue";
import { useCategoryStore, useFoodStore, useTagStore, useToolStore } from "~/composables/store";
import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue";
import { IngredientFood, RecipeCategory, RecipeTag, RecipeTool } from "~/lib/api/types/recipe";
import { NoUndefinedField } from "~/lib/api/types/non-generated";
import { useLazyRecipes } from "~/composables/recipes";
import { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
import { usePublicCategoryStore } from "~/composables/store/use-category-store";
import { usePublicFoodStore } from "~/composables/store/use-food-store";
import { usePublicTagStore } from "~/composables/store/use-tag-store";
import { usePublicToolStore } from "~/composables/store/use-tool-store";
export default defineComponent({
components: { SearchFilter, RecipeCardSection },
props: {
groupSlug: {
type: String,
required: true,
},
},
setup(props) {
const router = useRouter();
const { $auth, $globals, i18n } = useContext();
const loggedIn = computed(() => {
return $auth.loggedIn;
});
const state = ref({
auto: true,
search: "",
orderBy: "created_at",
orderDirection: "desc" as "asc" | "desc",
// and/or
requireAllCategories: false,
requireAllTags: false,
requireAllTools: false,
requireAllFoods: false,
});
const { recipes, appendRecipes, assignSorted, removeRecipe, replaceRecipes } = useLazyRecipes(loggedIn.value ? null : props.groupSlug);
const categories = loggedIn.value ? useCategoryStore() : usePublicCategoryStore(props.groupSlug);
const selectedCategories = ref<NoUndefinedField<RecipeCategory>[]>([]);
const foods = loggedIn.value ? useFoodStore() : usePublicFoodStore(props.groupSlug);
const selectedFoods = ref<IngredientFood[]>([]);
const tags = loggedIn.value ? useTagStore() : usePublicTagStore(props.groupSlug);
const selectedTags = ref<NoUndefinedField<RecipeTag>[]>([]);
const tools = loggedIn.value ? useToolStore() : usePublicToolStore(props.groupSlug);
const selectedTools = ref<NoUndefinedField<RecipeTool>[]>([]);
const passedQuery = ref<RecipeSearchQuery | null>(null);
function reset() {
state.value.search = "";
state.value.orderBy = "created_at";
state.value.orderDirection = "desc";
state.value.requireAllCategories = false;
state.value.requireAllTags = false;
state.value.requireAllTools = false;
state.value.requireAllFoods = false;
selectedCategories.value = [];
selectedFoods.value = [];
selectedTags.value = [];
selectedTools.value = [];
router.push({
query: {},
});
search();
}
function toggleOrderDirection() {
state.value.orderDirection = state.value.orderDirection === "asc" ? "desc" : "asc";
}
function toIDArray(array: { id: string }[]) {
return array.map((item) => item.id);
}
function hideKeyboard() {
input.value.blur()
}
const input: Ref<any> = ref(null);
async function search() {
await router.push({
query: {
categories: toIDArray(selectedCategories.value),
foods: toIDArray(selectedFoods.value),
tags: toIDArray(selectedTags.value),
tools: toIDArray(selectedTools.value),
// Only add the query param if it's or not default
...{
auto: state.value.auto ? undefined : "false",
search: state.value.search === "" ? undefined : state.value.search,
orderBy: state.value.orderBy === "createdAt" ? undefined : state.value.orderBy,
orderDirection: state.value.orderDirection === "desc" ? undefined : state.value.orderDirection,
requireAllCategories: state.value.requireAllCategories ? "true" : undefined,
requireAllTags: state.value.requireAllTags ? "true" : undefined,
requireAllTools: state.value.requireAllTools ? "true" : undefined,
requireAllFoods: state.value.requireAllFoods ? "true" : undefined,
},
},
});
passedQuery.value = {
search: state.value.search,
categories: toIDArray(selectedCategories.value),
foods: toIDArray(selectedFoods.value),
tags: toIDArray(selectedTags.value),
tools: toIDArray(selectedTools.value),
requireAllCategories: state.value.requireAllCategories,
requireAllTags: state.value.requireAllTags,
requireAllTools: state.value.requireAllTools,
requireAllFoods: state.value.requireAllFoods,
orderBy: state.value.orderBy,
orderDirection: state.value.orderDirection,
_searchSeed: Date.now().toString()
};
}
function waitUntilAndExecute(
condition: () => boolean,
callback: () => void,
opts = { timeout: 2000, interval: 500 }
): Promise<void> {
return new Promise((resolve, reject) => {
const state = {
timeout: undefined as number | undefined,
interval: undefined as number | undefined,
};
const check = () => {
if (condition()) {
clearInterval(state.interval);
clearTimeout(state.timeout);
callback();
resolve();
}
};
// For some reason these were returning NodeJS.Timeout
state.interval = setInterval(check, opts.interval) as unknown as number;
state.timeout = setTimeout(() => {
clearInterval(state.interval);
reject(new Error("Timeout"));
}, opts.timeout) as unknown as number;
});
}
const sortText = computed(() => {
const sort = sortable.find((s) => s.value === state.value.orderBy);
if (!sort) return "";
return `${sort.name}`;
});
const sortable = [
{
icon: $globals.icons.orderAlphabeticalAscending,
name: i18n.tc("general.sort-alphabetically"),
value: "name",
},
{
icon: $globals.icons.newBox,
name: i18n.tc("general.created"),
value: "created_at",
},
{
icon: $globals.icons.chefHat,
name: i18n.tc("general.last-made"),
value: "last_made",
},
{
icon: $globals.icons.star,
name: i18n.tc("general.rating"),
value: "rating",
},
{
icon: $globals.icons.update,
name: i18n.tc("general.updated"),
value: "update_at",
},
{
icon: $globals.icons.diceMultiple,
name: i18n.tc("general.random"),
value: "random",
},
];
onMounted(() => {
// Hydrate Search
// wait for stores to be hydrated
// read query params
const query = router.currentRoute.query;
if (query.auto) {
state.value.auto = query.auto === "true";
}
if (query.search) {
state.value.search = query.search as string;
}
if (query.orderBy) {
state.value.orderBy = query.orderBy as string;
}
if (query.orderDirection) {
state.value.orderDirection = query.orderDirection as "asc" | "desc";
}
const promises: Promise<void>[] = [];
if (query.categories) {
promises.push(
waitUntilAndExecute(
() => categories.items.value.length > 0,
() => {
const result = categories.items.value.filter((item) =>
(query.categories as string[]).includes(item.id as string)
);
selectedCategories.value = result as NoUndefinedField<RecipeCategory>[];
}
)
);
}
if (query.foods) {
promises.push(
waitUntilAndExecute(
() => {
if (foods.foods.value) {
return foods.foods.value.length > 0;
}
return false;
},
() => {
const result = foods.foods.value?.filter((item) => (query.foods as string[]).includes(item.id));
selectedFoods.value = result ?? [];
}
)
);
}
if (query.tags) {
promises.push(
waitUntilAndExecute(
() => tags.items.value.length > 0,
() => {
const result = tags.items.value.filter((item) => (query.tags as string[]).includes(item.id as string));
selectedTags.value = result as NoUndefinedField<RecipeTag>[];
}
)
);
}
if (query.tools) {
promises.push(
waitUntilAndExecute(
() => tools.items.value.length > 0,
() => {
const result = tools.items.value.filter((item) => (query.tools as string[]).includes(item.id));
selectedTools.value = result as NoUndefinedField<RecipeTool>[];
}
)
);
}
Promise.allSettled(promises).then(() => {
search();
});
});
watchDebounced(
[
() => state.value.search,
() => state.value.requireAllCategories,
() => state.value.requireAllTags,
() => state.value.requireAllTools,
() => state.value.requireAllFoods,
() => state.value.orderBy,
() => state.value.orderDirection,
selectedCategories,
selectedFoods,
selectedTags,
selectedTools,
],
async () => {
if (state.value.auto) {
await search();
}
},
{
debounce: 500,
}
);
return {
sortText,
search,
reset,
state,
categories: categories.items as unknown as NoUndefinedField<RecipeCategory>[],
tags: tags.items as unknown as NoUndefinedField<RecipeTag>[],
foods: foods.foods,
tools: tools.items as unknown as NoUndefinedField<RecipeTool>[],
sortable,
toggleOrderDirection,
hideKeyboard,
input,
selectedCategories,
selectedFoods,
selectedTags,
selectedTools,
appendRecipes,
assignSorted,
recipes,
removeRecipe,
replaceRecipes,
passedQuery,
};
},
head: {},
});
</script>
<style lang="css">
.search-row {
display: flex;
flex-wrap: wrap;
gap: 0.65rem;
margin-top: 1rem;
}
.search-container {
display: flex;
justify-content: center;
}
.search-box {
width: 950px;
}
.search-button-container {
margin: 3rem auto 0 auto;
max-width: 500px;
}
</style>

View File

@@ -32,6 +32,7 @@
<v-autocomplete
v-model="value.unit"
:search-input.sync="unitSearch"
auto-select-first
hide-details
dense
solo
@@ -59,6 +60,7 @@
<v-autocomplete
v-model="value.food"
:search-input.sync="foodSearch"
auto-select-first
hide-details
dense
solo
@@ -99,21 +101,10 @@
hover
:large="false"
class="my-auto"
:buttons="[
{
icon: $globals.icons.delete,
text: $tc('general.delete'),
event: 'delete',
},
{
icon: $globals.icons.dotsVertical,
text: $tc('general.menu'),
event: 'open',
children: contextMenuOptions,
},
]"
:buttons="btns"
@toggle-section="toggleTitle"
@toggle-original="toggleOriginalText"
@insert-ingredient="$emit('insert-ingredient')"
@delete="$emit('delete')"
/>
</div>
@@ -143,9 +134,68 @@ export default defineComponent({
type: Boolean,
default: false,
},
allowInsertIngredient: {
type: Boolean,
default: false,
}
},
setup(props) {
const { i18n } = useContext();
setup(props, { listeners }) {
const { i18n, $globals } = useContext();
const contextMenuOptions = computed(() => {
const options = [
{
text: i18n.tc("recipe.toggle-section"),
event: "toggle-section",
},
];
if (props.allowInsertIngredient) {
options.push({
text: i18n.tc("recipe.insert-ingredient") ,
event: "insert-ingredient",
})
}
// FUTURE: add option to parse a single ingredient
// if (!value.food && !value.unit && value.note) {
// options.push({
// text: "Parse Ingredient",
// event: "parse-ingredient",
// });
// }
if (props.value.originalText) {
options.push({
text: i18n.tc("recipe.see-original-text"),
event: "toggle-original",
});
}
return options;
});
const btns = computed(() => {
const out = [
{
icon: $globals.icons.dotsVertical,
text: i18n.tc("general.menu"),
event: "open",
children: contextMenuOptions.value,
},
];
if (listeners && listeners.delete) {
// @ts-expect-error - TODO: fix this
out.unshift({
icon: $globals.icons.delete,
text: i18n.tc("general.delete"),
event: "delete",
});
}
return out;
});
// ==================================================
// Foods
@@ -155,8 +205,7 @@ export default defineComponent({
async function createAssignFood() {
foodData.data.name = foodSearch.value;
await foodStore.actions.createOne(foodData.data);
props.value.food = foodStore.foods.value?.find((food) => food.name === foodSearch.value);
props.value.food = await foodStore.actions.createOne(foodData.data) || undefined;
foodData.reset();
}
@@ -168,8 +217,7 @@ export default defineComponent({
async function createAssignUnit() {
unitsData.data.name = unitSearch.value;
await unitStore.actions.createOne(unitsData.data);
props.value.unit = unitStore.units.value?.find((unit) => unit.name === unitSearch.value);
props.value.unit = await unitStore.actions.createOne(unitsData.data) || undefined;
unitsData.reset();
}
@@ -209,32 +257,6 @@ export default defineComponent({
}
}
const contextMenuOptions = computed(() => {
const options = [
{
text: i18n.t("recipe.toggle-section") as string,
event: "toggle-section",
},
];
// FUTURE: add option to parse a single ingredient
// if (!value.food && !value.unit && value.note) {
// options.push({
// text: "Parse Ingredient",
// event: "parse-ingredient",
// });
// }
if (props.value.originalText) {
options.push({
text: i18n.t("recipe.see-original-text") as string,
event: "toggle-original",
});
}
return options;
});
function quantityFilter(e: KeyboardEvent) {
// if digit is pressed, add to quantity
if (e.key === "-" || e.key === "+" || e.key === "e") {
@@ -259,6 +281,7 @@ export default defineComponent({
unitSearch,
validators,
workingUnitData: unitsData.data,
btns,
};
},
});

View File

@@ -1,9 +1,11 @@
<template>
<div v-html="markup"></div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="safeMarkup"></div>
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
import { computed, defineComponent } from "@nuxtjs/composition-api";
import { sanitizeIngredientHTML } from "~/composables/recipes/use-recipe-ingredients";
export default defineComponent({
props: {
markup: {
@@ -11,5 +13,11 @@ export default defineComponent({
required: true,
},
},
setup(props) {
const safeMarkup = computed(() => sanitizeIngredientHTML(props.markup));
return {
safeMarkup,
}
}
});
</script>

View File

@@ -0,0 +1,61 @@
<template>
<div class="ma-0 pa-0 text-subtitle-1 dense-markdown ingredient-item">
<SafeMarkdown v-if="parsedIng.quantity" class="d-inline" :source="parsedIng.quantity" />
<template v-if="parsedIng.unit">{{ parsedIng.unit }} </template>
<SafeMarkdown v-if="parsedIng.note && !parsedIng.name" class="text-bold d-inline" :source="parsedIng.note" />
<template v-else>
<SafeMarkdown v-if="parsedIng.name" class="text-bold d-inline" :source="parsedIng.name" />
<SafeMarkdown v-if="parsedIng.note" class="note" :source="parsedIng.note" />
</template>
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from "@nuxtjs/composition-api";
import { RecipeIngredient } from "~/lib/api/types/group";
import { useParsedIngredientText } from "~/composables/recipes";
export default defineComponent({
props: {
ingredient: {
type: Object as () => RecipeIngredient,
required: true,
},
disableAmount: {
type: Boolean,
default: false,
},
scale: {
type: Number,
default: 1,
},
},
setup(props) {
const parsedIng = computed(() => {
return useParsedIngredientText(props.ingredient, props.disableAmount, props.scale);
});
return {
parsedIng,
};
},
});
</script>
<style lang="scss">
.ingredient-item {
.d-inline {
& > p {
display: inline;
}
}
.text-bold {
font-weight: bold;
}
}
.note {
line-height: 1.25em;
font-size: 0.8em;
opacity: 0.7;
}
</style>

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