Compare commits

..

74 Commits

Author SHA1 Message Date
renovate[bot]
a41ad8c6ed chore(deps): update dependency mypy to v1.11.1 (#3963)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 19:26:12 -05:00
João Antunes
9c38c89c44 Update swag.md (#3961) 2024-07-30 21:19:18 +00:00
renovate[bot]
998440d064 fix(deps): update dependency pydantic-settings to v2.4.0 (#3960)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 17:14:12 +00:00
renovate[bot]
b01d12c377 chore(deps): update dependency pre-commit to v3.8.0 (#3954)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 12:02:42 -05:00
renovate[bot]
1dee574a08 fix(deps): update dependency pillow-heif to ^0.18.0 (#3949)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 11:40:42 -05:00
renovate[bot]
257c4461a3 fix(deps): update dependency rapidfuzz to v3.9.5 (#3959)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 11:12:17 -05:00
renovate[bot]
01f4257190 fix(deps): update dependency apprise to v1.8.1 (#3942)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 16:00:07 +00:00
renovate[bot]
d7b7dd6c83 fix(deps): update dependency recipe-scrapers to v15 (#3955)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2024-07-30 17:48:47 +02:00
github-actions[bot]
23c2eab682 fix(auto): Update pre-commit hooks (#3957)
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-07-29 10:41:26 +00:00
Michael Genson
def346d16d fix: Reduce search tolerance on organizers page (#3950) 2024-07-28 04:23:26 +00:00
tyme-dev
cc324b29ae fix: Homepage icon and text update (#3922)
Co-authored-by: Timothy Pace <Timothy@timothy.dev>
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-07-28 04:08:34 +00:00
Michael Genson
9d58f9b266 fix: Offline Shopping List Fixes V2 - Electric Boogaloo (#3837)
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2024-07-27 21:25:58 -05:00
renovate[bot]
30b2776f3c fix(deps): update dependency openai to v1.37.1 (#3943)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-26 12:27:34 +02:00
Hayden
60d23d0686 chore(l10n): New Crowdin updates (#3944) 2024-07-26 10:15:33 +00:00
Hayden
edf649dea6 fix: prevent postgres credentials leak (#3895)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-07-25 20:27:50 +00:00
renovate[bot]
29b4a3cd22 chore(deps): update dependency ruff to v0.5.5 (#3940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 20:07:09 +00:00
renovate[bot]
f3a5148628 fix(deps): update dependency bcrypt to v4.2.0 (#3928)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 11:55:26 -08:00
renovate[bot]
33abd777e0 chore(deps): update dependency pytest to v8.3.2 (#3939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 11:22:12 +00:00
Hayden
739055caf6 chore(l10n): New Crowdin updates (#3938) 2024-07-25 21:10:07 +10:00
Kuchenpirat
8c29bd3439 fix: task py:migrate description (#3932) 2024-07-23 15:43:21 +00:00
renovate[bot]
2c4d0b692b chore(deps): update dependency mkdocs-material to v9.5.30 (#3930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-23 17:31:01 +02:00
Brian Choromanski
946b79b77a feat: PWA Additions (#3896)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-07-23 17:04:15 +02:00
Hayden
cd154d09b2 chore(l10n): New Crowdin updates (#3929) 2024-07-23 07:26:34 +10:00
github-actions[bot]
236c930b54 fix(auto): Update pre-commit hooks (#3925)
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-07-22 18:08:24 +00:00
renovate[bot]
980c847e36 fix(deps): update dependency openai to v1.37.0 (#3927)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-22 12:58:29 -05:00
renovate[bot]
91700771e6 chore(deps): update dependency pylint to v3.2.6 (#3923)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-21 22:02:55 +00:00
Hayden
abb6ad5fd0 chore(l10n): New Crowdin updates (#3921) 2024-07-21 16:50:51 -05:00
renovate[bot]
ac7af02f77 chore(deps): update dependency pytest to v8.3.1 (#3916)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-20 22:39:57 -05:00
Hayden
525b398687 chore(l10n): New Crowdin updates (#3917) 2024-07-21 03:08:27 +00:00
renovate[bot]
fafc836ccc chore(deps): update dependency ruff to v0.5.4 (#3915)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-20 21:58:11 -05:00
renovate[bot]
c617b829e5 fix(deps): update dependency openai to v1.36.1 (#3919)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-20 21:42:13 -05:00
Michael Genson
5b1e827d45 fix: Convert Daily Schedule Time to UTC (#3914) 2024-07-20 21:57:02 +00:00
boc-the-git
e33b62be2a docs: Change allow signup to false (#3913) 2024-07-20 10:38:57 -05:00
Arsène Reymond
60c33b499c feat: Internationalize sent emails (#3818) 2024-07-20 10:32:24 +00:00
Hayden
c205dff523 chore(l10n): New Crowdin updates (#3911) 2024-07-20 10:19:38 +00:00
renovate[bot]
ce69899c4b fix(deps): update dependency uvicorn to v0.30.3 (#3912)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-20 20:09:31 +10:00
renovate[bot]
a4183e3453 chore(deps): update dependency mypy to v1.11.0 (#3910)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-19 17:27:56 -05:00
renovate[bot]
ab39408a24 fix(deps): update dependency openai to v1.36.0 (#3909)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-19 12:09:20 -05:00
boc-the-git
8c6c98483c docs: Remove duplicated "step 2" (#3908) 2024-07-19 14:32:40 +02:00
renovate[bot]
ae095ab572 chore(deps): update dependency ruff to v0.5.3 (#3905)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-18 16:58:55 -05:00
renovate[bot]
65356bc21a fix(deps): update dependency openai to v1.35.15 (#3906)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-18 22:42:34 +02:00
renovate[bot]
3aed5de3fc chore(deps): update dependency pytest-asyncio to v0.23.8 (#3901)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-17 18:29:24 -05:00
renovate[bot]
a4e9e54dae fix(deps): update dependency fastapi to v0.111.1 (#3891)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-17 09:51:28 -05:00
renovate[bot]
8f698e437e fix(deps): update dependency recipe-scrapers to v14.58.0 (#3894)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-17 11:34:48 +00:00
boc-the-git
ab0d36825a fix: Create directory used for Docker Secrets (#3888) 2024-07-17 06:24:53 -05:00
renovate[bot]
4e2f6c57f1 fix(deps): update dependency openai to v1.35.14 (#3897)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-16 09:10:37 +02:00
renovate[bot]
de4cb8ba83 chore(deps): update dependency mkdocs-material to v9.5.29 (#3889)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 10:35:31 +00:00
github-actions[bot]
375f43c596 fix(auto): Update pre-commit hooks (#3893)
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-07-15 10:26:27 +00:00
renovate[bot]
3034945e7e chore(deps): update dependency ruff to v0.5.2 (#3890)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 20:16:07 +10:00
Hayden
8e5effa532 chore(l10n): New Crowdin updates (#3887) 2024-07-13 15:40:41 +02:00
Litchi Pi
3b81d3b18a fix: Use env variable to get alembic config file in exporter (#3882)
Signed-off-by: Litchi Pi <litchi.pi@proton.me>
2024-07-12 12:18:06 +00:00
Michael Genson
d0f8b5773d fix: Bump other version numbers in GH workflow (#3840) 2024-07-12 11:30:22 +00:00
renovate[bot]
14910162dc chore(deps): update dependency coverage to v7.6.0 (#3884)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-12 21:00:35 +10:00
Hayden
035f780d27 chore(l10n): New Crowdin updates (#3886) 2024-07-11 22:22:37 +02:00
renovate[bot]
f10161ee92 fix(deps): update dependency recipe-scrapers to v14.57.1 (#3883)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 09:56:19 -05:00
Michael Genson
b1a100a8c5 feat: Push On Hand Items to Bottom Of Add To Shopping List Dialog (#3862)
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-07-10 20:41:15 +00:00
Hayden
7db39d32d1 chore(l10n): New Crowdin updates (#3878) 2024-07-10 15:24:34 -05:00
renovate[bot]
10921f9a64 fix(deps): update dependency openai to v1.35.13 (#3877)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-10 13:48:02 -05:00
renovate[bot]
ba1c44172e fix(deps): update dependency tzdata to v2024 (#3825)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-10 17:01:27 +00:00
zeskeertwee
fd2dc15a15 fix: Follow redirects during scraping (#3875) 2024-07-10 16:49:13 +00:00
renovate[bot]
47124488bb fix(deps): update dependency pydantic to v2.8.2 (#3844)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-10 10:10:21 -05:00
Marcus Noble
6e680c972a feat: Show recipe tags on mobile view and meal plan (#3864)
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
2024-07-10 09:43:33 +00:00
renovate[bot]
1fd2eb37ae fix(deps): update dependency openai to v1.35.12 (#3873)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-10 19:33:45 +10:00
Hayden
923a59791a chore(l10n): New Crowdin updates (#3869) 2024-07-09 10:12:16 +00:00
Carter
1fcc2c755a fix: Add a default value of list when a user's group is None (#3872) 2024-07-08 21:13:04 -05:00
Michael Genson
d5f7a883df fix: Make Mealie Timezone-Aware (#3847)
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-07-08 21:12:20 +00:00
Michael Genson
17f9eef551 docs: Update API Docs (#3856) 2024-07-08 10:01:31 +00:00
Elijah Mock
ca1ab33291 docs: Fix FAQ typos (#3866) 2024-07-07 19:11:53 -05:00
Michael Genson
6e6ae80c46 fix: Restore Webhook Test Functionality (#3857)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-07-06 19:10:01 +00:00
Hayden
aa6e109162 chore(l10n): New Crowdin updates (#3860) 2024-07-06 21:01:58 +02:00
renovate[bot]
a6e4b778c1 fix(deps): update dependency openai to v1.35.10 (#3843)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-05 19:16:40 +00:00
github-actions[bot]
31c7cb7906 docs(auto): Update image tag, for release v1.10.2 (#3851)
Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-07-05 19:07:22 +00:00
Michael Genson
d954b5cf48 fix: Minor Typo (#3855)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-07-05 18:58:52 +00:00
renovate[bot]
e5c2f5570f chore(deps): update dependency ruff to v0.5.1 (#3854)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-05 10:43:22 -05:00
213 changed files with 2246 additions and 1005 deletions

View File

@@ -38,3 +38,6 @@ RUN apt-get update \
libwebp-dev \
libsasl2-dev libldap2-dev libssl-dev \
gnupg gnupg2 gnupg1
# create directory used for Docker Secrets
RUN mkdir -p /run/secrets

View File

@@ -27,6 +27,7 @@
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
},
"extensions": [
"charliermarsh.ruff",
"dbaeumer.vscode-eslint",
"matangover.mypy",
"ms-python.black-formatter",

View File

@@ -61,10 +61,15 @@ jobs:
- name: Checkout 🛎
uses: actions/checkout@v4
- name: Extract Version From Tag Name
run: echo "VERSION_NUM=$(echo ${{ github.event.release.tag_name }} | sed 's/^v//')" >> $GITHUB_ENV
- name: Modify version strings
run: |
sed -i 's/:v[0-9]*.[0-9]*.[0-9]*/:${{ github.event.release.tag_name }}/' docs/docs/documentation/getting-started/installation/sqlite.md
sed -i 's/:v[0-9]*.[0-9]*.[0-9]*/:${{ github.event.release.tag_name }}/' docs/docs/documentation/getting-started/installation/postgres.md
sed -i 's/:v[0-9]*.[0-9]*.[0-9]*/:v${{ env.VERSION_NUM }}/' docs/docs/documentation/getting-started/installation/sqlite.md
sed -i 's/:v[0-9]*.[0-9]*.[0-9]*/:v${{ env.VERSION_NUM }}/' docs/docs/documentation/getting-started/installation/postgres.md
sed -i 's/^version = "[^"]*"/version = "${{ env.VERSION_NUM }}"/' pyproject.toml
sed -i 's/^\s*"version": "[^"]*"/"version": "${{ env.VERSION_NUM }}"/' frontend/package.json
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6

32
.github/workflows/scheduled-checks.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Scheduled Checks
on:
schedule:
# Every monday at 7 AM
- cron: 0 7 * * 1
jobs:
update:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout 🛎
uses: actions/checkout@v4
- name: Update pre-commit Hooks
uses: vrslev/pre-commit-autoupdate@v1.0.0
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
# This doesn't currently work for us because it creates the PR but the workflows don't run.
# TODO: Provide a personal access token as a parameter here, that solves that problem.
# https://github.com/peter-evans/create-pull-request
with:
commit-message: "Update pre-commit hooks"
branch: "fix/update-pre-commit-hooks"
delete-branch: true
base: mealie-next
title: "fix(auto): Update pre-commit hooks"
body: "Auto-generated by `.github/workflows/scheduled-checks.yml`"

View File

@@ -12,6 +12,6 @@ repos:
exclude: ^tests/data/
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.5.0
rev: v0.5.5
hooks:
- id: ruff-format

View File

@@ -148,7 +148,7 @@ tasks:
- poetry run python mealie/app.py
py:migrate:
desc: generates a new database migration file e.g. task py:migrate "add new column"
desc: generates a new database migration file e.g. task py:migrate -- "add new column"
cmds:
- poetry run alembic revision --autogenerate -m "{{ .CLI_ARGS }}"
- task: py:format

View File

@@ -13,7 +13,7 @@ from text_unidecode import unidecode
import mealie.db.migration_types
from alembic import op
from mealie.db.models._model_utils import GUID
from mealie.db.models._model_utils.guid import GUID
# revision identifiers, used by Alembic.
revision = "5ab195a474eb"

View File

@@ -6,7 +6,7 @@ Create Date: 2024-03-18 02:28:15.896959
"""
from datetime import datetime
from datetime import datetime, timezone
from textwrap import dedent
from typing import Any
from uuid import uuid4
@@ -34,7 +34,7 @@ def new_user_rating(user_id: Any, recipe_id: Any, rating: float | None = None, i
else:
id = "%.32x" % uuid4().int
now = datetime.now().isoformat()
now = datetime.now(timezone.utc).isoformat()
return {
"id": id,
"user_id": user_id,

View File

@@ -92,6 +92,9 @@ RUN apt-get update \
libldap-2.5 \
&& rm -rf /var/lib/apt/lists/*
# create directory used for Docker Secrets
RUN mkdir -p /run/secrets
# copying poetry and venv into image
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH

View File

@@ -9,7 +9,7 @@ How exactly you need to modify it is of course highly contextual to the change y
## Using Alembic to generate upgrade script
In your dev container you can run something like (change the message) `task py:migrate "Add creation tag to group preferences"` to have Alembic generate an upgrade script for you.
In your dev container you can run something like (change the message) `task py:migrate -- "Add creation tag to group preferences"` to have Alembic generate an upgrade script for you.
The script Alembic generates, will be limited! (Perhaps there's a way to resolve that? Haven't looked into it yet)
For example, Alembic generated a script _similar_ to this (it has been modified already to have accurate foreign key names, for instance):

View File

@@ -48,7 +48,7 @@ services:
restart: unless-stopped
```
Don't forget to change the <code>mydomain.duckns</code> into your personal domain and the <code>duckdnstoken</code> into your token and remove the brackets.
Don't forget to change the <code>mydomain.duckdns</code> into your personal domain and the <code>duckdnstoken</code> into your token and remove the brackets.
## Step 3: Change the config files

View File

@@ -2,14 +2,14 @@
## 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?
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 recipes 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)
4. If your Foods database is empty, click `Seed` and choose your language. You should end up with a list of foods. (Wait a 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`)
@@ -33,9 +33,9 @@ Do the following for each recipe you want to intelligently handle ingredients.
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?
## 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.
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. Note 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.
- [Migration From v0.5.x](./migrating-to-mealie-v1.md)
@@ -45,7 +45,7 @@ You can change the theme by settings the environment variables.
- [Backend Config - Themeing](./installation/backend-config.md#themeing)
## How can I change the Login Session Timeout?
## How can I change the login session timeout?
Login session can be configured by setting the `TOKEN_TIME` variable on the backend container.
@@ -53,7 +53,7 @@ Login session can be configured by setting the `TOKEN_TIME` variable on the back
## Can I serve Mealie on a subpath?
No. Due to limitations from the Javascript Framework, mealie doesn't support serving Mealie on a subpath.
No. Due to limitations from the JavaScript Framework, Mealie doesn't support serving Mealie on a subpath.
## Can I install Mealie without docker?
@@ -130,8 +130,8 @@ stateDiagram-v2
For more information, check out the [Permissions and Public Access guide](./usage/permissions-and-public-access.md).
## 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.
## 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:
```
@@ -139,11 +139,11 @@ 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 on 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 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 on Meal Plan data to remind you to defrost the chicken, marinate 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 control 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.
## 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 providing automatic daily backups that export your data in json, plain-text markdown files, and/or custom Jinja2 templates. **This puts you in control 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?

View File

@@ -4,22 +4,22 @@
### General
| Variables | Default | Description |
| ----------------------------- | :-------------------: | ----------------------------------------------------------------------------------- |
| 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 |
| 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** |
| API_DOCS | True | Turns on/off access to the API documentation locally. |
| TZ | UTC | Must be set to get correct date/time on the server |
| ALLOW_SIGNUP<super>\*</super> | false | Allow user sign-up without token |
| LOG_CONFIG_OVERRIDE | | Override the config for logging with a custom path |
| LOG_LEVEL | info | Logging level (e.g. critical, error, warning, info, debug, trace) |
| DAILY_SCHEDULE_TIME | 23:45 | The time of day to run the daily tasks. |
| Variables | Default | Description |
| ----------------------------- | :-------------------: | --------------------------------------------------------------------------------------------------------- |
| 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 |
| 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** |
| API_DOCS | True | Turns on/off access to the API documentation locally |
| TZ | UTC | Must be set to get correct date/time on the server |
| ALLOW_SIGNUP<super>\*</super> | false | Allow user sign-up without token |
| LOG_CONFIG_OVERRIDE | | Override the config for logging with a custom path |
| LOG_LEVEL | info | Logging level (e.g. critical, error, warning, info, debug, trace) |
| DAILY_SCHEDULE_TIME | 23:45 | The time of day to run daily server tasks, in HH:MM format. Use the server's local time, *not* UTC |
<super>\*</super> Starting in v1.4.0 this was changed to default to `false` as apart of a security review of the application.
<super>\*</super> Starting in v1.4.0 this was changed to default to `false` as part of a security review of the application.
### Security

View File

@@ -58,7 +58,7 @@ The following steps were tested on a Ubuntu 20.04 server, but should work for mo
4. Create a docker-compose.yaml file in the mealie directory: `touch docker-compose.yaml`
5. Use the text editor of 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.
## Step 3: 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:
@@ -67,7 +67,7 @@ After you've decided setup the files it's important to set a few ENV variables t
- [x] You've set the [`BASE_URL`](./backend-config.md#general) variable.
- [x] You've set the `DEFAULT_EMAIL` and `DEFAULT_GROUP` variable.
## Step 3: Startup
## Step 4: 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`.
@@ -87,11 +87,11 @@ You should see the containers start up without error. You should now be able to
**Password:** MyPassword
## Step 4: Validate Installation
## Step 5: Validate Installation
After the startup is complete, you should see a login screen. Use the default credentials above to log in 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.
## Step 5: Backup
## Step 6: 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.

View File

@@ -7,7 +7,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In
```yaml
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v1.10.1 # (3)
image: ghcr.io/mealie-recipes/mealie:v1.10.2 # (3)
container_name: mealie
restart: always
ports:
@@ -20,7 +20,7 @@ services:
- mealie-data:/app/data/
environment:
# Set Backend ENV Variables Here
ALLOW_SIGNUP: true
ALLOW_SIGNUP: false
PUID: 1000
PGID: 1000
TZ: America/Anchorage

View File

@@ -11,7 +11,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
```yaml
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v1.10.1 # (3)
image: ghcr.io/mealie-recipes/mealie:v1.10.2 # (3)
container_name: mealie
restart: always
ports:
@@ -24,7 +24,7 @@ services:
- mealie-data:/app/data/
environment:
# Set Backend ENV Variables Here
ALLOW_SIGNUP: true
ALLOW_SIGNUP: false
PUID: 1000
PGID: 1000
TZ: America/Anchorage

File diff suppressed because one or more lines are too long

View File

@@ -18,8 +18,6 @@
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

@@ -36,6 +36,9 @@
<v-list-item-subtitle>
<SafeMarkdown :source="description" />
</v-list-item-subtitle>
<div class="d-flex flex-wrap justify-start">
<RecipeChips :truncate="true" :items="tags" :title="false" :limit="2" :small="true" url-prefix="tags" />
</div>
<div class="d-flex flex-wrap justify-end align-center">
<slot name="actions">
<RecipeFavoriteBadge v-if="isOwnGroup && showRecipeContent" :recipe-id="recipeId" show-always />
@@ -83,6 +86,7 @@ import RecipeFavoriteBadge from "./RecipeFavoriteBadge.vue";
import RecipeContextMenu from "./RecipeContextMenu.vue";
import RecipeCardImage from "./RecipeCardImage.vue";
import RecipeRating from "./RecipeRating.vue";
import RecipeChips from "./RecipeChips.vue";
import { useLoggedInState } from "~/composables/use-logged-in-state";
export default defineComponent({
@@ -91,6 +95,7 @@ export default defineComponent({
RecipeContextMenu,
RecipeRating,
RecipeCardImage,
RecipeChips,
},
props: {
name: {
@@ -114,6 +119,10 @@ export default defineComponent({
required: false,
default: "abc123",
},
tags: {
type: Array,
default: () => [],
},
recipeId: {
type: String,
required: true,

View File

@@ -237,30 +237,40 @@ export default defineComponent({
}
});
let currentTitle = "";
const onHandIngs: ShoppingListIngredient[] = [];
const shoppingListIngredientSections = shoppingListIngredients.reduce((sections, ing) => {
// if title append new section to the end of the array
if (ing.ingredient.title) {
currentTitle = ing.ingredient.title;
}
// If this is the first item in the section, create a new section
if (sections.length === 0 || currentTitle !== sections[sections.length - 1].sectionName) {
if (sections.length) {
// Add the on-hand ingredients to the previous section
sections[sections.length - 1].ingredients.push(...onHandIngs);
onHandIngs.length = 0;
}
sections.push({
sectionName: ing.ingredient.title,
ingredients: [ing],
sectionName: currentTitle,
ingredients: [],
});
}
// Store the on-hand ingredients for later
if (ing.ingredient.food?.onHand) {
onHandIngs.push(ing);
return sections;
}
// append new section if first
if (sections.length === 0) {
sections.push({
sectionName: "",
ingredients: [ing],
});
return sections;
}
// otherwise add ingredient to last section in the array
// Add the ingredient to previous section
sections[sections.length - 1].ingredients.push(ing);
return sections;
}, [] as ShoppingListIngredientSection[]);
// Add remaining on-hand ingredients to the previous section
shoppingListIngredientSections[shoppingListIngredientSections.length - 1].ingredients.push(...onHandIngs);
recipeSectionMap.set(recipe.slug, {
recipeId: recipe.id,
recipeName: recipe.name,

View File

@@ -126,8 +126,8 @@
<RecipeCardSection
v-if="state.ready"
class="mt-n5"
:icon="$globals.icons.search"
:title="$tc('search.results')"
:icon="$globals.icons.silverwareForkKnife"
:title="$tc('general.recipes')"
:recipes="recipes"
:query="passedQueryWithSeed"
@replaceRecipes="replaceRecipes"

View File

@@ -102,7 +102,7 @@
<v-icon left>
{{ $globals.icons.calendar }}
</v-icon>
{{ $t('recipe.last-made-date', { date: value ? new Date(value+"Z").toLocaleDateString($i18n.locale) : $t("general.never") } ) }}
{{ $t('recipe.last-made-date', { date: value ? new Date(value).toLocaleDateString($i18n.locale) : $t("general.never") } ) }}
</v-chip>
</div>
</div>
@@ -199,11 +199,7 @@ export default defineComponent({
await userApi.recipes.updateLastMade(props.recipe.slug, newTimelineEvent.value.timestamp);
// update recipe in parent so the user can see it
// we remove the trailing "Z" since this is how the API returns it
context.emit(
"input", newTimelineEvent.value.timestamp
.substring(0, newTimelineEvent.value.timestamp.length - 1)
);
context.emit("input", newTimelineEvent.value.timestamp);
}
// update the image, if provided

View File

@@ -114,9 +114,9 @@ export default defineComponent({
options: {
ignoreLocation: true,
shouldSort: true,
threshold: 0.6,
threshold: 0.2,
location: 0,
distance: 100,
distance: 20,
findAllMatches: true,
maxPatternLength: 32,
minMatchCharLength: 1,

View File

@@ -8,7 +8,7 @@
<template v-if="!useMobileFormat" #opposite>
<v-chip v-if="event.timestamp" label large>
<v-icon class="mr-1"> {{ $globals.icons.calendar }} </v-icon>
{{ new Date(event.timestamp+"Z").toLocaleDateString($i18n.locale) }}
{{ new Date(event.timestamp).toLocaleDateString($i18n.locale) }}
</v-chip>
</template>
<v-card
@@ -25,7 +25,7 @@
<v-col v-if="useMobileFormat" align-self="center" class="pr-0">
<v-chip label>
<v-icon> {{ $globals.icons.calendar }} </v-icon>
{{ new Date(event.timestamp+"Z").toLocaleDateString($i18n.locale) }}
{{ new Date(event.timestamp || "").toLocaleDateString($i18n.locale) }}
</v-chip>
</v-col>
<v-col v-else cols="9" style="margin: auto; text-align: center;">

View File

@@ -69,13 +69,13 @@
</v-row>
<v-row v-if="!listItem.checked && recipeList && recipeList.length && displayRecipeRefs" no-gutters class="mb-2">
<v-col cols="auto" style="width: 100%;">
<RecipeList :recipes="recipeList" :list-item="listItem" :disabled="isOffline" small tile />
<RecipeList :recipes="recipeList" :list-item="listItem" :disabled="$nuxt.isOffline" small tile />
</v-col>
</v-row>
<v-row v-if="listItem.checked" no-gutters class="mb-2">
<v-col cols="auto">
<div class="text-caption font-weight-light font-italic">
{{ $t("shopping-list.completed-on", {date: new Date(listItem.updateAt+"Z").toLocaleDateString($i18n.locale)}) }}
{{ $t("shopping-list.completed-on", {date: new Date(listItem.updateAt || "").toLocaleDateString($i18n.locale)}) }}
</div>
</v-col>
</v-row>
@@ -136,10 +136,6 @@ export default defineComponent({
type: Map<string, RecipeSummary>,
default: undefined,
},
isOffline: {
type: Boolean,
default: false,
},
},
setup(props, context) {
const { i18n } = useContext();

View File

@@ -157,9 +157,9 @@
const topLinks = computed<SidebarLinks>(() => [
{
icon: $globals.icons.search,
icon: $globals.icons.silverwareForkKnife,
to: `/g/${groupSlug.value}`,
title: i18n.tc("sidebar.search"),
title: i18n.tc("general.recipes"),
restricted: true,
},
{

View File

@@ -64,7 +64,6 @@ export const useGroupWebhooks = function () {
newDt.setMinutes(Number(minutes));
updateData.scheduledTime = `${pad(newDt.getUTCHours(), 2)}:${pad(newDt.getUTCMinutes(), 2)}`;
console.log(updateData.scheduledTime);
const payload = {
...updateData,
@@ -85,7 +84,14 @@ export const useGroupWebhooks = function () {
if (data) {
this.refreshAll();
}
loading.value = false;
},
async testOne(id: string | number) {
loading.value = true;
await api.groupWebhooks.testOne(id);
loading.value = false;
}
};
const webhooks = actions.getAll();

View File

@@ -1,10 +1,11 @@
import { computed, ref } from "@nuxtjs/composition-api";
import { computed, reactive, watch } from "@nuxtjs/composition-api";
import { useLocalStorage } from "@vueuse/core";
import { useUserApi } from "~/composables/api";
import { ShoppingListItemOut } from "~/lib/api/types/group";
import { ShoppingListItemOut, ShoppingListOut } from "~/lib/api/types/group";
import { RequestResponse } from "~/lib/api/types/non-generated";
const localStorageKey = "shopping-list-queue";
const queueTimeout = 48 * 60 * 60 * 1000; // 48 hours
const queueTimeout = 5 * 60 * 1000; // 5 minutes
type ItemQueueType = "create" | "update" | "delete";
@@ -23,27 +24,73 @@ interface Storage {
export function useShoppingListItemActions(shoppingListId: string) {
const api = useUserApi();
const storage = useLocalStorage(localStorageKey, {} as Storage, { deep: true });
const queue = storage.value[shoppingListId] ||= { create: [], update: [], delete: [], lastUpdate: Date.now()};
const queue = reactive(getQueue());
const queueEmpty = computed(() => !queue.create.length && !queue.update.length && !queue.delete.length);
if (queueEmpty.value) {
queue.lastUpdate = Date.now();
}
const isOffline = ref(false);
storage.value[shoppingListId] = { ...queue }
watch(
() => queue,
(value) => {
storage.value[shoppingListId] = { ...value }
},
{
deep: true,
immediate: true,
},
)
function removeFromQueue(queue: ShoppingListItemOut[], item: ShoppingListItemOut): boolean {
const index = queue.findIndex(i => i.id === item.id);
function isValidQueueObject(obj: any): obj is ShoppingListQueue {
if (typeof obj !== "object" || obj === null) {
return false;
}
const hasRequiredProps = "create" in obj && "update" in obj && "delete" in obj && "lastUpdate" in obj;
if (!hasRequiredProps) {
return false;
}
const arraysValid = Array.isArray(obj.create) && Array.isArray(obj.update) && Array.isArray(obj.delete);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const lastUpdateValid = typeof obj.lastUpdate === "number" && !isNaN(new Date(obj.lastUpdate).getTime());
return arraysValid && lastUpdateValid;
}
function createEmptyQueue(): ShoppingListQueue {
const newQueue = { create: [], update: [], delete: [], lastUpdate: Date.now() };
return newQueue;
}
function getQueue(): ShoppingListQueue {
try {
const fetchedQueue = storage.value[shoppingListId];
if (!isValidQueueObject(fetchedQueue)) {
console.log("Invalid queue object in local storage; resetting queue.");
return createEmptyQueue();
} else {
return fetchedQueue;
}
} catch (error) {
console.log("Error validating queue object in local storage; resetting queue.", error);
return createEmptyQueue();
}
}
function removeFromQueue(itemQueue: ShoppingListItemOut[], item: ShoppingListItemOut): boolean {
const index = itemQueue.findIndex(i => i.id === item.id);
if (index === -1) {
return false;
}
queue.splice(index, 1);
itemQueue.splice(index, 1);
return true;
}
async function getList() {
const response = await api.shopping.lists.getOne(shoppingListId);
handleResponse(response);
return response.data;
}
@@ -90,44 +137,59 @@ export function useShoppingListItemActions(shoppingListId: string) {
if (itemQueueType === "delete" || itemQueueType === "all") {
queue.delete = itemIds ? queue.delete.filter(item => !itemIds.includes(item.id)) : [];
}
if (queueEmpty.value) {
queue.lastUpdate = Date.now();
}
}
// Set the storage value explicitly so changes are saved in the browser.
storage.value[shoppingListId] = { ...queue };
function checkUpdateState(list: ShoppingListOut) {
const cutoffDate = new Date(queue.lastUpdate + queueTimeout).toISOString();
if (list.updateAt && list.updateAt > cutoffDate) {
// If the queue is too far behind the shopping list to reliably do updates, we clear the queue
console.log("Out of sync with server; clearing queue");
clearQueueItems("all");
}
}
/**
* Handles the response from the backend and sets the isOffline flag if necessary.
* Processes the queue items and returns whether the processing was successful.
*/
function handleResponse(response: any) {
// TODO: is there a better way of checking for network errors?
isOffline.value = response.error?.message?.includes("Network Error") || false;
}
async function processQueueItems(
action: (items: ShoppingListItemOut[]) => Promise<any>,
action: (items: ShoppingListItemOut[]) => Promise<RequestResponse<any>>,
itemQueueType: ItemQueueType,
) {
const queueItems = getQueueItems(itemQueueType);
if (!queueItems.length) {
return;
): Promise<boolean> {
let queueItems: ShoppingListItemOut[];
try {
queueItems = getQueueItems(itemQueueType);
if (!queueItems.length) {
return true;
}
} catch (error) {
console.log(`Error fetching queue items of type ${itemQueueType}:`, error);
clearQueueItems(itemQueueType);
return false;
}
const itemsToProcess = [...queueItems];
await action(itemsToProcess)
.then((response) => {
handleResponse(response);
if (!isOffline.value) {
clearQueueItems(itemQueueType, itemsToProcess.map(item => item.id));
}
});
try {
const itemsToProcess = [...queueItems];
await action(itemsToProcess)
.then(() => {
if (window.$nuxt.isOnline) {
clearQueueItems(itemQueueType, itemsToProcess.map(item => item.id));
}
});
} catch (error) {
console.log(`Error processing queue items of type ${itemQueueType}:`, error);
clearQueueItems(itemQueueType);
return false;
}
return true;
}
async function process() {
if(
!queue.create.length &&
!queue.update.length &&
!queue.delete.length
) {
if(queueEmpty.value) {
queue.lastUpdate = Date.now();
return;
}
@@ -135,26 +197,23 @@ export function useShoppingListItemActions(shoppingListId: string) {
if (!data) {
return;
}
checkUpdateState(data);
const cutoffDate = new Date(queue.lastUpdate + queueTimeout).toISOString();
if (data.updateAt && data.updateAt > cutoffDate) {
// If the queue is too far behind the shopping list to reliably do updates, we clear the queue
clearQueueItems("all");
} else {
// We send each bulk request one at a time, since the backend may merge items
await processQueueItems((items) => api.shopping.items.deleteMany(items), "delete");
await processQueueItems((items) => api.shopping.items.updateMany(items), "update");
await processQueueItems((items) => api.shopping.items.createMany(items), "create");
}
// We send each bulk request one at a time, since the backend may merge items
// "failures" here refers to an actual error, rather than failing to reach the backend
let failures = 0;
if (!(await processQueueItems((items) => api.shopping.items.deleteMany(items), "delete"))) failures++;
if (!(await processQueueItems((items) => api.shopping.items.updateMany(items), "update"))) failures++;
if (!(await processQueueItems((items) => api.shopping.items.createMany(items), "create"))) failures++;
// If we're online, the queue is fully processed, so we're up to date
if (!isOffline.value) {
// If we're online, or the queue is empty, the queue is fully processed, so we're up to date
// Otherwise, if all three queue processes failed, we've already reset the queue, so we need to reset the date
if (window.$nuxt.isOnline || queueEmpty.value || failures === 3) {
queue.lastUpdate = Date.now();
}
}
return {
isOffline,
getList,
createItem,
updateItem,

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissies",
"administrator": "Administrateur",
"user-can-invite-other-to-group": "Gebruiker kan ander na groep nooi",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Gebruiker kan groep bestuur",
"user-can-organize-group-data": "Gebruiker kan groepdata organiseer",
"enable-advanced-features": "Aktiveer gevorderde funksies",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Права",
"administrator": "Администратор",
"user-can-invite-other-to-group": "Потребителя може да добавя други в групата",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Потребителя може да управлява групата",
"user-can-organize-group-data": "Потребителя може да организира данните на групата",
"enable-advanced-features": "Включване на разширени функции",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permisos",
"administrator": "Administrador",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -145,11 +145,11 @@
"save": "Uložit",
"settings": "Nastavení",
"share": "Sdílet",
"show-all": "Show All",
"show-all": "Zobrazit vše",
"shuffle": "Náhodně",
"sort": "Seřadit",
"sort-ascending": "Sort Ascending",
"sort-descending": "Sort Descending",
"sort-ascending": "Řadit vzestupně",
"sort-descending": "Řadit sestupně",
"sort-alphabetically": "Abecedně",
"status": "Stav",
"subject": "Předmět",
@@ -161,7 +161,7 @@
"test": "Test",
"themes": "Motivy",
"thursday": "Čtvrtek",
"title": "Title",
"title": "Název",
"token": "Token",
"tuesday": "Úterý",
"type": "Typ",
@@ -208,7 +208,7 @@
"upload-file": "Nahrát soubor",
"created-on-date": "Vytvořeno dne: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard.",
"clipboard-copy-failure": "Zkopírování do schránky se nezdařilo.",
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
"organizers": "Organizers",
"caution": "Caution"
@@ -247,7 +247,7 @@
"group-preferences": "Nastavení skupiny",
"private-group": "Soukromá skupina",
"private-group-description": "Setting your group to private will default all public view options to default. This overrides an individual recipes public view settings.",
"enable-public-access": "Enable Public Access",
"enable-public-access": "Povolit veřejný přístup",
"enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in",
"allow-users-outside-of-your-group-to-see-your-recipes": "Povolit uživatelům mimo vaši skupinu vidět vaše recepty",
"allow-users-outside-of-your-group-to-see-your-recipes-description": "When enabled you can use a public share link to share specific recipes without authorizing the user. When disabled, you can only share recipes with users who are in your group or with a pre-generated private link",
@@ -794,10 +794,10 @@
"food": "Jídlo",
"note": "Poznámka",
"label": "Popisek",
"save-label": "Save Label",
"save-label": "Uložit štítek",
"linked-item-warning": "Tato položka je propojena s jedním nebo více recepty. Úprava jednotky nebo jídla bude mít neočekávané důsledky při přidání nebo odebrání receptu z tohoto seznamu.",
"toggle-food": "Přepnout typ položky",
"manage-labels": "Manage Labels",
"manage-labels": "Spravovat štítky",
"are-you-sure-you-want-to-delete-this-item": "Are you sure you want to delete this item?",
"copy-as-text": "Copy as Text",
"copy-as-markdown": "Copy as Markdown",
@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",
@@ -1018,9 +1018,9 @@
"labels": {
"seed-dialog-text": "Naplnit databázi s běžnými popisky používanými ve vašem jazyce.",
"edit-label": "Upravit štítek",
"new-label": "New Label",
"new-label": "Nový štítek",
"labels": "Štítky",
"assign-label": "Assign Label"
"assign-label": "Přiřadit štítek"
},
"recipes": {
"purge-exports": "Purge Exports",

View File

@@ -211,7 +211,7 @@
"clipboard-copy-failure": "Kopiering til udklipsholderen mislykkedes.",
"confirm-delete-generic-items": "Er du sikker på at du ønsker at slette de valgte emner?",
"organizers": "Organisatorer",
"caution": "Caution"
"caution": "Bemærk"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Er du sikker på, du vil slette <b>{groupName}<b/>?",
@@ -292,8 +292,8 @@
"mealplan-updated": "Madplanen blev ændret",
"no-meal-plan-defined-yet": "Ingen madplan er defineret",
"no-meal-planned-for-today": "Ingen ret er planlagt til i dag",
"numberOfDays-hint": "Number of days on page load",
"numberOfDays-label": "Default Days",
"numberOfDays-hint": "Antal dage ved sideindlæsning",
"numberOfDays-label": "Standarddage",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Kun opskrifter med disse kategorier vil blive brugt i madplaner",
"planner": "Planlæg madplan",
"quick-week": "Hurtig uge",
@@ -383,7 +383,7 @@
},
"recipekeeper": {
"title": "Recipe Keeper",
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
"description-long": "Mealie kan importere opskrifter fra Recipe Keeper. Eksportér dine opskrifter i zip-format, og upload derefter .zip-filen nedenfor."
}
},
"new-recipe": {
@@ -449,8 +449,8 @@
"ingredients": "Ingredienser",
"insert-ingredient": "Indsæt Ingrediens",
"insert-section": "Indsæt sektion",
"insert-above": "Insert Above",
"insert-below": "Insert Below",
"insert-above": "Indsæt ovenover",
"insert-below": "Indsæt nedenunder",
"instructions": "Instruktioner",
"key-name-required": "Nøglenavn påkrævet",
"landscape-view-coming-soon": "Liggende visning (Kommer snart)",
@@ -586,8 +586,8 @@
"report-deletion-failed": "Sletning af rapport mislykkedes",
"recipe-debugger": "Fejlsøgning af opskrifter",
"recipe-debugger-description": "Indsæt URL'en på hjemmesiden, der indeholder den opskrift, du vil fejlsøge. URL-adressen vil blive læst og resultaterne vil blive vist. Hvis ingen data bliver vist, er indhentning af opskrifter fra hjemmesiden endnu ikke understøttet af Mealie.",
"use-openai": "Use OpenAI",
"recipe-debugger-use-openai-description": "Use OpenAI to parse the results instead of relying on the scraper library. When creating a recipe via URL, this is done automatically if the scraper library fails, but you may test it manually here.",
"use-openai": "Brug OpenAI",
"recipe-debugger-use-openai-description": "Brug OpenAI til at fortolke resultaterne i stedet for at stole på scraper biblioteket. Når du opretter en opskrift via URL, gøres dette automatisk, hvis skraberbiblioteket fejler, men du kan teste det manuelt her.",
"debug": "Fejlsøgning",
"tree-view": "Træ visning",
"recipe-yield": "Udbytte af opskrift",
@@ -640,7 +640,7 @@
"backup-created-at-response-export_path": "Backup oprettet ved {path}",
"backup-deleted": "Backup slettet",
"restore-success": "Gendannelse lykkedes",
"restore-fail": "Restore failed. Check your server logs for more details",
"restore-fail": "Gendannelse mislykkedes. Tjek dine serverlogs for flere detaljer",
"backup-tag": "Backupnavn",
"create-heading": "Opret en backup",
"delete-backup": "Slet backup",
@@ -810,11 +810,11 @@
"items-checked-count": "Ingen elementer markeret|Et element markeret|{count} elementer er markeret",
"no-label": "Ingen etiket",
"completed-on": "Afsluttet den {date}",
"you-are-offline": "You are offline",
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?"
"you-are-offline": "Du er offline",
"you-are-offline-description": "Ikke alle funktioner er tilgængelige mens offline. Du kan stadig tilføje, modificere, og fjerne elementer, men du vil ikke kunne synkronisere dine ændringer til serveren, før du er online igen.",
"are-you-sure-you-want-to-check-all-items": "Er du sikker på, at du vil markere alle elementer?",
"are-you-sure-you-want-to-uncheck-all-items": "Er du sikker på, at du vil fjerne markeringen af alle elementer?",
"are-you-sure-you-want-to-delete-checked-items": "Er du sikker på, at du vil sletter de valgte elementer?"
},
"sidebar": {
"all-recipes": "Alle opskr.",
@@ -955,7 +955,7 @@
"user-details": "Brugerdetaljer",
"user-name": "Brugernavn",
"authentication-method": "Godkendelsesmetode",
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"authentication-method-hint": "Dette angiver, hvordan en bruger vil logge ind på Mealie. Hvis du ikke er sikker, vælg 'Mealie'",
"permissions": "Rettigheder",
"administrator": "Administrator",
"user-can-invite-other-to-group": "Bruger kan invitere andre til gruppen",
@@ -990,8 +990,8 @@
"food-data": "Oplysninger om fødevare",
"example-food-singular": "fx.: grøntsag",
"example-food-plural": "fx.: grøntsager",
"label-overwrite-warning": "This will assign the chosen label to all selected foods and potentially overwrite your existing labels.",
"on-hand-checkbox-label": "Setting this flag will make this food unchecked by default when adding a recipe to a shopping list."
"label-overwrite-warning": "Dette vil tildele den valgte etiket til alle udvalgte fødevarer og potentielt overskrive dine eksisterende etiketter.",
"on-hand-checkbox-label": "Ændring af dette flag ændrer markeringen, så denne fødevare ikke er markeret som standard, når du tilføjer en opskrift til en indkøbsliste."
},
"units": {
"seed-dialog-text": "Opret standard enheder i dit sprog.",
@@ -1020,7 +1020,7 @@
"edit-label": "Redigér etiket",
"new-label": "Ny etiket",
"labels": "Etiketter",
"assign-label": "Assign Label"
"assign-label": "Tildel etiket"
},
"recipes": {
"purge-exports": "Tøm Eksport",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Αυτό καθορίζει τον τρόπο με τον οποίο ένας χρήστης θα ταυτοποιηθεί με το Mealie. Αν δεν είστε σίγουροι, επιλέξτε 'Mealie'",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "Ο χρήστης μπορεί να προσκαλέσει άλλους στην ομάδα",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Ενεργοποίηση χαρακτηριστικών για προχωρημένους",

View File

@@ -268,7 +268,7 @@
"group-management": "Group Management",
"admin-group-management": "Admin Group Management",
"admin-group-management-text": "Changes to this group will be reflected immediately.",
"group-id-value": "Group Id: {0}"
"group-id-value": "Group ID: {0}"
},
"meal-plan": {
"create-a-new-meal-plan": "Create a New Meal Plan",
@@ -283,18 +283,18 @@
"meal-planner": "Meal Planner",
"meal-plans": "Meal Plans",
"mealplan-categories": "MEALPLAN CATEGORIES",
"mealplan-created": "Mealplan created",
"mealplan-creation-failed": "Mealplan creation failed",
"mealplan-created": "Meal plan created",
"mealplan-creation-failed": "Meal plan creation failed",
"mealplan-deleted": "Mealplan deleted",
"mealplan-deletion-failed": "Mealplan deletion failed",
"mealplan-settings": "Mealplan Settings",
"mealplan-update-failed": "Mealplan update failed",
"mealplan-updated": "Mealplan Updated",
"mealplan-deletion-failed": "Meal plan deletion failed",
"mealplan-settings": "Meal plan settings",
"mealplan-update-failed": "Meal plan update failed",
"mealplan-updated": "Meal plan updated",
"no-meal-plan-defined-yet": "No meal plan defined yet",
"no-meal-planned-for-today": "No meal planned for today",
"numberOfDays-hint": "Number of days on page load",
"numberOfDays-label": "Default Days",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Only recipes with these categories will be used in Meal Plans",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Only the recipes with these categories will be used in Meal Plans",
"planner": "Planner",
"quick-week": "Quick Week",
"side": "Side",
@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie'",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie'",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permisos",
"administrator": "Administrador",
"user-can-invite-other-to-group": "El usuario puede invitar a otros al grupo",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "El usuario puede administrar el grupo",
"user-can-organize-group-data": "El usuario puede organizar los datos del grupo",
"enable-advanced-features": "Habilitar Características Avanzadas",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Tämä määrittelee, miten käyttäjä todentaa Mealien. Jos et ole varma, valitse 'Mealie'",
"permissions": "Käyttöoikeudet",
"administrator": "Ylläpitäjä",
"user-can-invite-other-to-group": "Käyttäjä voi kutsua muita ryhmään",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Käyttäjä voi hallita ryhmää",
"user-can-organize-group-data": "Käyttäjä voi järjestellä ryhmän tietoja",
"enable-advanced-features": "Salli edistyneemmät ominaisuudet",

View File

@@ -810,8 +810,8 @@
"items-checked-count": "Aucun élément coché|Un élément coché|{count} éléments cochés",
"no-label": "Aucune étiquette",
"completed-on": "Terminé le {date}",
"you-are-offline": "You are offline",
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
"you-are-offline": "Vous êtes hors-ligne",
"you-are-offline-description": "Certaines fonctionnalités ne sont pas disponibles lorsque vous êtes hors-ligne. Vous pouvez toujours ajouter, modifier et supprimer des éléments, mais il ne sera pas possible de synchroniser les changements avec le serveur tant que vous ne serez pas en ligne.",
"are-you-sure-you-want-to-check-all-items": "Voulez-vous vraiment sélectionner tous les éléments ?",
"are-you-sure-you-want-to-uncheck-all-items": "Voulez-vous vraiment désélectionner tous les éléments ?",
"are-you-sure-you-want-to-delete-checked-items": "Voulez-vous vraiment supprimer tous les éléments sélectionnés ?"
@@ -958,7 +958,7 @@
"authentication-method-hint": "Ceci infique comment un utilisateur va s'authentifier sur Mealie. Si vous n'êtes pas sûr, choisissez 'Mealie'",
"permissions": "Autorisations",
"administrator": "Administrateur",
"user-can-invite-other-to-group": "L'utilisateur peut inviter quelqu'un au groupe",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "L'utilisateur peut gérer le groupe",
"user-can-organize-group-data": "L'utilisateur peut organiser des données de groupe",
"enable-advanced-features": "Activer les fonctions avancées",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Ceci infique comment un utilisateur va s'authentifier sur Mealie. Si vous n'êtes pas sûr, choisissez 'Mealie'",
"permissions": "Autorisations",
"administrator": "Administrateur",
"user-can-invite-other-to-group": "L'utilisateur peut inviter quelqu'un au groupe",
"user-can-invite-other-to-group": "Lutilisateur peut inviter dautres personnes dans le groupe",
"user-can-manage-group": "L'utilisateur peut gérer le groupe",
"user-can-organize-group-data": "L'utilisateur peut organiser des données de groupe",
"enable-advanced-features": "Activer les fonctions avancées",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "זה מציין איך משתמש יתחבר ל״מילי״. אם אתה לא בטוח, בחר מילי",
"permissions": "הרשאות",
"administrator": "מנהל ראשי",
"user-can-invite-other-to-group": "משתמש יכול להזמין אחרים לקבוצה",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "משתמש יכול לנהל קבוצה",
"user-can-organize-group-data": "משתמש יכול לשנות מידע של קבוצה",
"enable-advanced-features": "אפשר אפשרויות מתקדמות",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Ovo određuje način autentifikacije korisnika u Mealie sustavu. Ako niste sigurni, odaberite 'Mealie",
"permissions": "Dozvole",
"administrator": "Administrator",
"user-can-invite-other-to-group": "Korisnik može pozvati druge u grupu",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Korisnik može upravljati grupom",
"user-can-organize-group-data": "Korisnik može organizirati podatke grupe",
"enable-advanced-features": "Omogućite napredne značajke",

View File

@@ -596,7 +596,7 @@
"screen-awake": "Képernyő ébren tartása",
"remove-image": "Kép etávolítása",
"nextStep": "Következő lépés",
"recipe-actions": "Recipe Actions",
"recipe-actions": "Receptekkel kapcsolatos tevékenységek",
"parser": {
"experimental-alert-text": "A Mealie természetes nyelvi feldolgozást használ a recept összetevőinek elemzésére, az egységek és az élelmiszerelemek létrehozására. Ez a funkció kísérleti jellegű, és előfordulhat, hogy nem mindig működik az elvárt módon. Ha nem szeretné használni az elemzett eredményeket, válassza a 'Mégse' lehetőséget, és a módosítások nem kerülnek mentésre.",
"ingredient-parser": "Hozzávaló elemző",
@@ -810,11 +810,11 @@
"items-checked-count": "Nincs ellenőrzött tétel|Egy ellenőrzött tétel|{count} ellenőrzött tétel",
"no-label": "Nincs címke",
"completed-on": "Teljesítve {date}",
"you-are-offline": "You are offline",
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?"
"you-are-offline": "Offline vagy",
"you-are-offline-description": "Offline állapotban nem minden funkció érhető el. Továbbra is hozzáadhat, módosíthat és eltávolíthat elemeket, de a módosításokat nem tudja szinkronizálni a szerverrel, amíg vissza nem tér az online állapotba.",
"are-you-sure-you-want-to-check-all-items": "Biztos, hogy minden elemet be akar jelölni?",
"are-you-sure-you-want-to-uncheck-all-items": "Biztos, hogy minden elem kijelölését visszavonja?",
"are-you-sure-you-want-to-delete-checked-items": "Biztosan törölni akarja az összes bejelölt elemet?"
},
"sidebar": {
"all-recipes": "Minden recept",
@@ -991,7 +991,7 @@
"example-food-singular": "pl. Hagyma",
"example-food-plural": "pl. Hagymák",
"label-overwrite-warning": "Ez a kiválasztott címkét az összes kiválasztott élelmiszerhez hozzárendeli, és esetleg felülírja a meglévő címkéket.",
"on-hand-checkbox-label": "Setting this flag will make this food unchecked by default when adding a recipe to a shopping list."
"on-hand-checkbox-label": "Ha ezt a jelzőt beállítja, akkor ez az élelmiszer alapértelmezés szerint nem lesz bejelölve, amikor egy receptet hozzáad egy bevásárlólistához."
},
"units": {
"seed-dialog-text": "Töltse be az Ön nyelve szerinti közös mennyiségi egységet tartalmazó adatbázist.",
@@ -1044,9 +1044,9 @@
"source-unit-will-be-deleted": "A forrás mennyiségi egység törlésre kerül"
},
"recipe-actions": {
"recipe-actions-data": "Recipe Actions Data",
"new-recipe-action": "New Recipe Action",
"edit-recipe-action": "Edit Recipe Action",
"recipe-actions-data": "Receptekkel kapcsolatos tevékenységek adatai",
"new-recipe-action": "Új recept tevékenység",
"edit-recipe-action": "Recept tevékenység szerkesztése",
"action-type": "Művelet típusa"
},
"create-alias": "Alias készítése",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie'",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -587,7 +587,7 @@
"recipe-debugger": "Debugger Ricetta",
"recipe-debugger-description": "Prendi l'URL della ricetta che vuoi fare il debug e incollalo qui. L'URL verrà recuperato dallo scraper di ricette e i risultati verranno visualizzati. Se non si vede alcun dato restituito, il sito che si sta cercando di analizzare non è supportato da Mealie o la sua libreria di scraping.",
"use-openai": "Usa OpenAI",
"recipe-debugger-use-openai-description": "Use OpenAI to parse the results instead of relying on the scraper library. When creating a recipe via URL, this is done automatically if the scraper library fails, but you may test it manually here.",
"recipe-debugger-use-openai-description": "Usa OpenAI per analizzare i risultati invece di affidarsi alla libreria scraper. Quando si crea una ricetta tramite URL, questo viene fatto automaticamente se la libreria scraper fallisce, ma è possibile testarlo manualmente qui.",
"debug": "Debug",
"tree-view": "Visualizzazione ad Albero",
"recipe-yield": "Resa Ricetta",
@@ -596,7 +596,7 @@
"screen-awake": "Mantieni lo schermo acceso",
"remove-image": "Rimuovi immagine",
"nextStep": "Passo successivo",
"recipe-actions": "Recipe Actions",
"recipe-actions": "Azioni Ricetta",
"parser": {
"experimental-alert-text": "Mealie utilizza l'elaborazione del linguaggio naturale per analizzare e creare unità e prodotti alimentari per i vostri ingredienti di ricetta. Questa funzione è sperimentale e potrebbe non funzionare sempre come previsto. Se preferisci non usare i risultati analizzati, puoi selezionare 'Annulla' e le tue modifiche non saranno salvate.",
"ingredient-parser": "Analizzatore ingredienti",
@@ -605,7 +605,7 @@
"select-parser": "Seleziona Analizzatore",
"natural-language-processor": "Analizzatore di Linguaggio Naturale",
"brute-parser": "Analizzatore brutale",
"openai-parser": "OpenAI Parser",
"openai-parser": "Parser OpenAI",
"parse-all": "Analizza tutto",
"no-unit": "Nessuna unità",
"missing-unit": "Crea unità mancante: {unit}",
@@ -640,7 +640,7 @@
"backup-created-at-response-export_path": "Backup Creato in {path}",
"backup-deleted": "Backup eliminato",
"restore-success": "Ripristino riuscito",
"restore-fail": "Restore failed. Check your server logs for more details",
"restore-fail": "Ripristino non riuscito. Controlla i log del tuo server per maggiori dettagli",
"backup-tag": "Tag Backup",
"create-heading": "Crea un Backup",
"delete-backup": "Elimina Backup",
@@ -778,9 +778,9 @@
"oidc-ready": "Pronto per OIDC",
"oidc-ready-error-text": "I valori OIDC non sono configurati. Questo può essere ignorato se non si utilizza Autenticazione OIDC.",
"oidc-ready-success-text": "Le variabili OIDC richieste sono tutte impostate.",
"openai-ready": "OpenAI Ready",
"openai-ready-error-text": "Not all OpenAI Values are configured. This can be ignored if you are not using OpenAI features.",
"openai-ready-success-text": "Required OpenAI variables are all set."
"openai-ready": "OpenAI Pronto",
"openai-ready-error-text": "Non tutti i valori OpenAI sono configurati. Puoi ignorarlo se non utilizzi le funzioni di OpenAI.",
"openai-ready-success-text": "Le variabili OpenAI richieste sono tutte impostate."
},
"shopping-list": {
"all-lists": "Tutte le Liste",
@@ -794,7 +794,7 @@
"food": "Alimenti",
"note": "Nota",
"label": "Etichetta",
"save-label": "Save Label",
"save-label": "Salva Etichetta",
"linked-item-warning": "Questo elemento è collegato a una o più ricette. La modifica delle unità o degli alimenti potrebbe dare risultati inattesi quando si aggiunge o si rimuove la ricetta da questo elenco.",
"toggle-food": "Attiva/Disattiva Alimento",
"manage-labels": "Gestisci Etichette",
@@ -810,11 +810,11 @@
"items-checked-count": "Nessun elemento selezionato|Un elemento selezionato|{count} elementi selezionati",
"no-label": "Nessuna etichetta",
"completed-on": "Completato il {date}",
"you-are-offline": "You are offline",
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?"
"you-are-offline": "Non sei in linea",
"you-are-offline-description": "Non tutte le funzioni sono disponibili quando non sei in linea. Puoi ancora aggiungere, modificare e rimuovere elementi, ma non sarai in grado di sincronizzare le modifiche con il server fino a quando non sarai di nuovo in linea.",
"are-you-sure-you-want-to-check-all-items": "Sei sicuro di voler tutti gli elementi?",
"are-you-sure-you-want-to-uncheck-all-items": "Sei sicuro di voler deselezionare tutti gli elementi?",
"are-you-sure-you-want-to-delete-checked-items": "Sei sicuro di voler rimuovere tutti gli articoli selezionati?"
},
"sidebar": {
"all-recipes": "Ricette",
@@ -958,7 +958,7 @@
"authentication-method-hint": "Indica come un utente si autenticherà con Mealie. Se non sei sicuro, scegli 'Mealie",
"permissions": "Permessi",
"administrator": "Amministratore",
"user-can-invite-other-to-group": "L'utente può invitare altri al gruppo",
"user-can-invite-other-to-group": "L'utente può invitare altre persone nel gruppo",
"user-can-manage-group": "L'utente può gestire il gruppo",
"user-can-organize-group-data": "L'utente può organizzare i dati del gruppo",
"enable-advanced-features": "Abilita funzionalità avanzate",
@@ -990,8 +990,8 @@
"food-data": "Dati Alimento",
"example-food-singular": "esempio: Cipolla",
"example-food-plural": "esempio: Cipolle",
"label-overwrite-warning": "This will assign the chosen label to all selected foods and potentially overwrite your existing labels.",
"on-hand-checkbox-label": "Setting this flag will make this food unchecked by default when adding a recipe to a shopping list."
"label-overwrite-warning": "Questo assegnerà l'etichetta scelta a tutti gli alimenti selezionati e potenzialmente sovrascriverà le etichette esistenti.",
"on-hand-checkbox-label": "Abilitando questa impostazione, l'alimento verrà deselezionato di default quando si aggiungerà una ricetta a una lista della spesa."
},
"units": {
"seed-dialog-text": "Riempie il database con unità comuni basate sulla lingua.",
@@ -1020,7 +1020,7 @@
"edit-label": "Modifica Etichetta",
"new-label": "Nuova Etichetta",
"labels": "Etichette",
"assign-label": "Assign Label"
"assign-label": "Assegna Etichetta"
},
"recipes": {
"purge-exports": "Elimina Esportazioni",
@@ -1044,10 +1044,10 @@
"source-unit-will-be-deleted": "L'unità di origine verrà eliminata"
},
"recipe-actions": {
"recipe-actions-data": "Recipe Actions Data",
"new-recipe-action": "New Recipe Action",
"edit-recipe-action": "Edit Recipe Action",
"action-type": "Action Type"
"recipe-actions-data": "Dati Azioni Ricetta",
"new-recipe-action": "Nuova Azione Ricetta",
"edit-recipe-action": "Modifica Azione Ricetta",
"action-type": "Tipo Di Azione"
},
"create-alias": "Crea Alias",
"manage-aliases": "Gestisci Alias",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "権限",
"administrator": "管理者",
"user-can-invite-other-to-group": "ユーザーは他のグループに招待できます",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "ユーザーはグループを管理できます",
"user-can-organize-group-data": "ユーザーはグループデータを整理できます",
"enable-advanced-features": "高度な機能を有効にする",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Leidimai",
"administrator": "Administratorius",
"user-can-invite-other-to-group": "Naudotojas gali kviesti kitus į grupę",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Naudotojas gali valdyti grupę",
"user-can-organize-group-data": "Naudotojas gali tvarkyti grupės duomenis",
"enable-advanced-features": "Įjungti pažangias funkcijas",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Dit bepaalt hoe een gebruiker zich aanmeldt bij Mealie. Als je het niet zeker weet, kies dan voor 'Mealie'",
"permissions": "Gebruikersrechten",
"administrator": "Beheerder",
"user-can-invite-other-to-group": "Gebruiker kan iemand uitnodigen voor de groep",
"user-can-invite-other-to-group": "Gebruiker kan anderen uitnodigen voor zijn groep",
"user-can-manage-group": "Gebruiker kan de groep beheren",
"user-can-organize-group-data": "Gebruiker kan groepsgegevens indelen",
"enable-advanced-features": "Geavanceerde functies inschakelen",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Rettigheter",
"administrator": "Administrator",
"user-can-invite-other-to-group": "Brukeren kan invitere andre til gruppe",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Brukeren kan administrere gruppe",
"user-can-organize-group-data": "Brukeren kan organisere gruppedata",
"enable-advanced-features": "Aktiver avanserte funksjoner",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Określa jak użytkownik będzie uwierzytelniać się z Mealie. Jeśli nie jesteś pewien, wybierz 'Mealie",
"permissions": "Uprawnienia",
"administrator": "Administrator",
"user-can-invite-other-to-group": "Użytkownik może zaprosić innych do grupy",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Użytkownik może zarządzać grupą",
"user-can-organize-group-data": "Użytkownik może organizować dane grupy",
"enable-advanced-features": "Włącz zaawansowane funkcje",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Isto especifica como um usuário se autentica com o Mealie. Se não tem certeza, escolha \"Mealie\"",
"permissions": "Permissões",
"administrator": "Administrador",
"user-can-invite-other-to-group": "O usuário pode convidar outro para o grupo",
"user-can-invite-other-to-group": "O usuário pode convidar outros para o grupo",
"user-can-manage-group": "Usuário pode gerenciar o grupo",
"user-can-organize-group-data": "Usuário pode organizar dados do grupo",
"enable-advanced-features": "Ativar recursos avançados",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissões",
"administrator": "Administrador",
"user-can-invite-other-to-group": "O utilizador pode convidar outro para o grupo",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "O utilizador pode gerir o grupo",
"user-can-organize-group-data": "O utilizador pode organizar dados do grupo",
"enable-advanced-features": "Habilitar recursos avançados",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Права доступа",
"administrator": "Администратор",
"user-can-invite-other-to-group": "Пользователь может пригласить других в группу",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Пользователь может управлять группой",
"user-can-organize-group-data": "Пользователь может менять групповые данные",
"enable-advanced-features": "Включить доп. функции",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Toto určuje ako bude overený užívateľ. Ak si nie ste istý, zvoľte 'Mealie'",
"permissions": "Povolenia",
"administrator": "Administrátor",
"user-can-invite-other-to-group": "Užívateľ môže do skupiny pozvať ďalších",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Užívateľ môže spravovať skupinu",
"user-can-organize-group-data": "Užívateľ môže spravovať údaje skupiny",
"enable-advanced-features": "Povoliť pokročilé funkcie",

View File

@@ -211,7 +211,7 @@
"clipboard-copy-failure": "Kopiranje na odložišče ni bilo uspešno.",
"confirm-delete-generic-items": "Ali ste prepričani, da želite izbrisati izbrane elemente?",
"organizers": "Organizatorji",
"caution": "Caution"
"caution": "Pozor"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Ste prepričani, da želite izbrisati <b>{groupName}<b/>?",
@@ -449,8 +449,8 @@
"ingredients": "Sestavine",
"insert-ingredient": "Dodaj sestavino",
"insert-section": "Vstavi odsek",
"insert-above": "Insert Above",
"insert-below": "Insert Below",
"insert-above": "Vstavi zgoraj",
"insert-below": "Vstavi spodaj",
"instructions": "Navodila",
"key-name-required": "Obvezen vnos imena ključa",
"landscape-view-coming-soon": "Ležeči pogled",
@@ -586,7 +586,7 @@
"report-deletion-failed": "Brisanje poročila ni bilo uspešno",
"recipe-debugger": "Odpravljanje težav v strganju recepta",
"recipe-debugger-description": "Prilepi povezavo do recepta, ki ga želiš postrgati. Strgalnik receptov bo postrgal spletno stran in prikazal rezultate. Če ne vidiš nobenih podatkov, potem spletna stran, ki jo želiš strgati ni podprta v Mealie oz. v strgalniku, ki ga uporablja.",
"use-openai": "Use OpenAI",
"use-openai": "Uporabi OpenAI",
"recipe-debugger-use-openai-description": "Use OpenAI to parse the results instead of relying on the scraper library. When creating a recipe via URL, this is done automatically if the scraper library fails, but you may test it manually here.",
"debug": "Debug",
"tree-view": "Drevesni prikaz",
@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Dovoljenja",
"administrator": "Administrator",
"user-can-invite-other-to-group": "Uporabnik lahko druge povabi v skupino",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Uporabnik lahko upravlja s skupino",
"user-can-organize-group-data": "Uporabnik lahko organizira podatke skupine",
"enable-advanced-features": "Vključi napredne funkcije",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Ово одређује како ће се корисник аутентификовати са Mили. Ако нисте сигурни, изаберите 'Mealie'",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -211,7 +211,7 @@
"clipboard-copy-failure": "Det gick inte att kopiera till urklipp.",
"confirm-delete-generic-items": "Är du säker på att du vill radera följande objekt?",
"organizers": "Organisatörer",
"caution": "Caution"
"caution": "Varning"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Är du säker på att du vill radera <b>{groupName}<b/>?",
@@ -449,8 +449,8 @@
"ingredients": "Ingredienser",
"insert-ingredient": "Infoga ingrediens",
"insert-section": "Infoga avdelning",
"insert-above": "Insert Above",
"insert-below": "Insert Below",
"insert-above": "Infoga Ovan",
"insert-below": "Infoga Nedan",
"instructions": "Instruktioner",
"key-name-required": "Nyckelnamn krävs",
"landscape-view-coming-soon": "Landskapsvy (kommer snart)",
@@ -810,11 +810,11 @@
"items-checked-count": "Inga artiklar markerade|En artikel markerad|{count} artiklar markerade",
"no-label": "Ingen etikett",
"completed-on": "Slutförd på {date}",
"you-are-offline": "You are offline",
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?"
"you-are-offline": "Du är offline",
"you-are-offline-description": "Alla funktioner är inte tillgängliga när du är offline. Du kan fortfarande lägga till, ändra och ta bort objekt, men du kommer ej kunna synka dina ändringar till servern förrän du är online igen.",
"are-you-sure-you-want-to-check-all-items": "Är du säker på att du vill markera alla objekt?",
"are-you-sure-you-want-to-uncheck-all-items": "Är du säker på att du vill avmarkera alla objekt?",
"are-you-sure-you-want-to-delete-checked-items": "Är du säker på att du vill ta bort alla markerade objekt?"
},
"sidebar": {
"all-recipes": "Recept",
@@ -990,8 +990,8 @@
"food-data": "Mat data",
"example-food-singular": "ex: Lök",
"example-food-plural": "ex: Lökar",
"label-overwrite-warning": "This will assign the chosen label to all selected foods and potentially overwrite your existing labels.",
"on-hand-checkbox-label": "Setting this flag will make this food unchecked by default when adding a recipe to a shopping list."
"label-overwrite-warning": "Detta kommer att tilldela den valda etiketten till alla utvalda måltider och potentiellt skriva över dina befintliga etiketter.",
"on-hand-checkbox-label": "Om du ställer in den här flaggan kommer de här livsmedlen att avmarkeras som standard när du lägger till ett recept i en inköpslista."
},
"units": {
"seed-dialog-text": "Fyll databasen med vanliga enheter baserade på ditt språk.",
@@ -1020,7 +1020,7 @@
"edit-label": "Redigera etikett",
"new-label": "Ny etikett",
"labels": "Etiketter",
"assign-label": "Assign Label"
"assign-label": "Tilldela etikett"
},
"recipes": {
"purge-exports": "Rensa exporter",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "Bu, bir kullanıcının Mealie ile nasıl kimlik doğrulaması yapacağını belirtir. Emin değilseniz, 'Mealie'yi seçin",
"permissions": "İzinler",
"administrator": "Yönetici",
"user-can-invite-other-to-group": "Kullanıcı başkalarını gruba davet edebilir",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "Kullanıcı grubu yönetebilir",
"user-can-organize-group-data": "Kullanıcı grup verilerini düzenleyebilir",
"enable-advanced-features": "Gelişmiş özellikleri etkinleştir",

View File

@@ -804,17 +804,17 @@
"delete-checked": "Видалити відмічене",
"toggle-label-sort": "Сортування міток",
"reorder-labels": "Перевпорядкувати мітки",
"uncheck-all-items": "Зняти вибір з усіх елементів",
"check-all-items": "Вибрати всі елементи",
"uncheck-all-items": "Зняти відмітку з усіх елементів",
"check-all-items": "Відмітити всі елементи",
"linked-recipes-count": "Нема пов'язаних рецептів|Один пов'язаний рецепт|{count} пов'язаних рецептів",
"items-checked-count": "Нема відмічених елементів|Один відмічений елемент|{count} елементів відмічено",
"no-label": "Без Мітки",
"completed-on": "Завершено {date}",
"you-are-offline": "You are offline",
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?"
"you-are-offline": "Ви не в мережі",
"you-are-offline-description": "Не всі функції доступні без мережі. Ви все ще можете додавати, змінювати та видаляти елементи, але не зможете синхронізувати зміни на сервер, поки під'єднаєтесь до мережі.",
"are-you-sure-you-want-to-check-all-items": "Ви впевнені, що хочете відмітити всі елементи?",
"are-you-sure-you-want-to-uncheck-all-items": "Ви впевнені, що хочете зняти відмітку з усіх елементів?",
"are-you-sure-you-want-to-delete-checked-items": "Ви впевнені, що хочете видалити всі відмічені елементи?"
},
"sidebar": {
"all-recipes": "Всі рецепти",
@@ -867,7 +867,7 @@
"create-a-tool": "Створити новий інструмент",
"tool-name": "Назва інструмента",
"create-new-tool": "Створити новий інструмент",
"on-hand-checkbox-label": "Показувати як в наявності (позначено)",
"on-hand-checkbox-label": "Показувати як в наявності (відмічене)",
"required-tools": "Необхідні інструменти",
"tool": "Інструмент"
},
@@ -991,7 +991,7 @@
"example-food-singular": "приклад: Цибулина",
"example-food-plural": "приклад: Цибуля",
"label-overwrite-warning": "Це призначить обрану мітку для всіх вибраних продуктів і потенційно перезапише існуючі мітки.",
"on-hand-checkbox-label": "Setting this flag will make this food unchecked by default when adding a recipe to a shopping list."
"on-hand-checkbox-label": "Встановлення цього параметра дозволить зробити цю їжу невідміченою за замовчуванням при додаванні рецепта до списку покупок."
},
"units": {
"seed-dialog-text": "Заповнити базу даних розповсюдженими одиницями виміру що відповідають мові.",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "权限",
"administrator": "管理员",
"user-can-invite-other-to-group": "用户可以邀请其他人加入群组",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "用户可以管理群组",
"user-can-organize-group-data": "用户可以整理群组数据",
"enable-advanced-features": "启用高级功能",

View File

@@ -958,7 +958,7 @@
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",
"user-can-invite-other-to-group": "User can invite others to group",
"user-can-manage-group": "User can manage group",
"user-can-organize-group-data": "User can organize group data",
"enable-advanced-features": "Enable advanced features",

View File

@@ -6,9 +6,15 @@ const prefix = "/api";
const routes = {
webhooks: `${prefix}/groups/webhooks`,
webhooksId: (id: string | number) => `${prefix}/groups/webhooks/${id}`,
webhooksIdTest: (id: string | number) => `${prefix}/groups/webhooks/${id}/test`,
};
export class WebhooksAPI extends BaseCRUDAPI<CreateWebhook, ReadWebhook> {
baseRoute = routes.webhooks;
itemRoute = routes.webhooksId;
itemTestRoute = routes.webhooksIdTest;
async testOne(itemId: string | number) {
return await this.requests.post<null>(`${this.itemTestRoute(itemId)}`, {});
}
}

View File

@@ -149,6 +149,7 @@ import {
mdiRotateRight,
mdiBookOpenPageVariant,
mdiFileCabinet,
mdiSilverwareForkKnife
} from "@mdi/js";
export const icons = {
@@ -249,6 +250,7 @@ export const icons = {
search: mdiMagnify,
shareVariant: mdiShareVariant,
shuffleVariant: mdiShuffleVariant,
silverwareForkKnife: mdiSilverwareForkKnife,
sort: mdiSort,
sortAscending: mdiSortAscending,
sortDescending: mdiSortDescending,

View File

@@ -351,13 +351,22 @@ export default {
},
manifest: {
start_url: "/",
scope: "/",
lang: "en",
dir: "auto",
name: "Mealie",
short_name: "Mealie",
id: "mealie",
description: "Mealie is a recipe management and meal planning app",
theme_color: process.env.THEME_LIGHT_PRIMARY || "#E58325",
background_color: "#FFFFFF",
display: "standalone",
display_override: [
"standalone",
"minimal-ui",
"browser",
"window-controls-overlay"
],
share_target: {
action: "/r/create/url",
method: "GET",
@@ -395,6 +404,56 @@ export default {
purpose: "maskable",
},
],
screenshots: [
{
"src": "/screenshots/home-narrow.png",
"sizes": "1600x2420",
"form_factor": "narrow",
"label": "Home Page"
},
{
"src": "/screenshots/recipe-narrow.png",
"sizes": "1600x2420",
"form_factor": "narrow",
"label": "Recipe Page"
},
{
"src": "/screenshots/editor-narrow.png",
"sizes": "1600x2420",
"form_factor": "narrow",
"label": "Editor Page"
},
{
"src": "/screenshots/parser-narrow.png",
"sizes": "1600x2420",
"form_factor": "narrow",
"label": "Parser Page"
},
{
"src": "/screenshots/home-wide.png",
"sizes": "2560x1460",
"form_factor": "wide",
"label": "Home Page"
},
{
"src": "/screenshots/recipe-wide.png",
"sizes": "2560x1460",
"form_factor": "wide",
"label": "Recipe Page"
},
{
"src": "/screenshots/editor-wide.png",
"sizes": "2560x1460",
"form_factor": "wide",
"label": "Editor Page"
},
{
"src": "/screenshots/parser-wide.png",
"sizes": "2560x1460",
"form_factor": "wide",
"label": "Parser Page"
}
],
"shortcuts": [
{
"name": "Shopping Lists",
@@ -403,8 +462,12 @@ export default {
"url": "/shopping-lists",
"icons": [
{
"src": "/svgs/mdiFormatListChecks.svg",
"sizes": "256x256",
"src": "/icons/mdiFormatListChecks-192x192.png",
"sizes": "192x192",
},
{
"src": "/icons/mdiFormatListChecks-96x96.png",
"sizes": "96x96",
}
]
},
@@ -415,12 +478,28 @@ export default {
"url": "/group/mealplan/planner/view",
"icons": [
{
"src": "/svgs/mdiCalendarMultiselect.svg",
"sizes": "256x256",
"src": "/icons/mdiCalendarMultiselect-192x192.png",
"sizes": "192x192",
},
{
"src": "/icons/mdiCalendarMultiselect-96x96.png",
"sizes": "96x96",
}
]
},
],
prefer_related_applications: false,
handle_links: "preferred",
orientation: "any",
categories: [
"food"
],
launch_handler: {
"client_mode": ["focus-existing", "auto"]
},
edge_side_panel: {
"preferred_width": 400
}
},
icon: false, // disables the icon module
},

View File

@@ -1,6 +1,6 @@
{
"name": "mealie",
"version": "1.0.0",
"version": "1.10.1",
"private": true,
"scripts": {
"dev": "nuxt",

View File

@@ -42,6 +42,7 @@
:slug="mealplan.recipe ? mealplan.recipe.slug : mealplan.title"
:description="mealplan.recipe ? mealplan.recipe.description : mealplan.text"
:name="mealplan.recipe ? mealplan.recipe.name : mealplan.title"
:tags="mealplan.recipe ? mealplan.recipe.tags : []"
/>
</div>
</v-col>

View File

@@ -10,8 +10,6 @@
</v-card-text>
</BasePageTitle>
<BannerExperimental />
<BaseButton create @click="actions.createOne()" />
<v-expansion-panels class="mt-2">
<v-expansion-panel v-for="(webhook, index) in webhooks" :key="index" class="my-2 left-border rounded">
@@ -36,6 +34,7 @@
:webhook="webhook"
@save="actions.updateOne($event)"
@delete="actions.deleteOne($event)"
@test="actions.testOne($event).then(() => alert.success($tc('events.test-message-sent')))"
/>
</v-expansion-panel-content>
</v-expansion-panel>
@@ -47,6 +46,7 @@
import { defineComponent } from "@nuxtjs/composition-api";
import { useGroupWebhooks, timeUTC } from "~/composables/use-group-webhooks";
import GroupWebhookEditor from "~/components/Domain/Group/GroupWebhookEditor.vue";
import { alert } from "~/composables/use-toast";
export default defineComponent({
components: { GroupWebhookEditor },
@@ -55,6 +55,7 @@ export default defineComponent({
const { actions, webhooks } = useGroupWebhooks();
return {
alert,
webhooks,
actions,
timeUTC

View File

@@ -28,7 +28,7 @@
<template #title> {{ shoppingList.name }} </template>
</BasePageTitle>
<BannerWarning
v-if="isOffline"
v-if="$nuxt.isOffline"
:title="$tc('shopping-list.you-are-offline')"
:description="$tc('shopping-list.you-are-offline-description')"
/>
@@ -46,7 +46,6 @@
:units="allUnits || []"
:foods="allFoods || []"
:recipes="recipeMap"
:is-offline="isOffline"
@checked="saveListItem"
@save="saveListItem"
@delete="deleteListItem(item)"
@@ -75,7 +74,6 @@
:units="allUnits || []"
:foods="allFoods || []"
:recipes="recipeMap"
:is-offline="isOffline"
@checked="saveListItem"
@save="saveListItem"
@delete="deleteListItem(item)"
@@ -132,7 +130,6 @@
:labels="allLabels || []"
:units="allUnits || []"
:foods="allFoods || []"
:is-offline="isOffline"
@delete="createEditorOpen = false"
@cancel="createEditorOpen = false"
@save="createListItem"
@@ -141,7 +138,7 @@
<div v-else class="mt-4 d-flex justify-end">
<BaseButton
v-if="preferences.viewByLabel" edit class="mr-2"
:disabled="isOffline"
:disabled="$nuxt.isOffline"
@click="toggleReorderLabelsDialog">
<template #icon> {{ $globals.icons.tags }} </template>
{{ $t('shopping-list.reorder-labels') }}
@@ -221,7 +218,6 @@
:labels="allLabels || []"
:units="allUnits || []"
:foods="allFoods || []"
:is-offline="isOffline"
@checked="saveListItem"
@save="saveListItem"
@delete="deleteListItem(item)"
@@ -244,10 +240,10 @@
{{ $tc('shopping-list.linked-recipes-count', shoppingList.recipeReferences ? shoppingList.recipeReferences.length : 0) }}
</div>
<v-divider class="my-4"></v-divider>
<RecipeList :recipes="Array.from(recipeMap.values())" show-description :disabled="isOffline">
<RecipeList :recipes="Array.from(recipeMap.values())" show-description :disabled="$nuxt.isOffline">
<template v-for="(recipe, index) in recipeMap.values()" #[`actions-${recipe.id}`]>
<v-list-item-action :key="'item-actions-decrease' + recipe.id">
<v-btn icon :disabled="isOffline" @click.prevent="removeRecipeReferenceToList(recipe.id)">
<v-btn icon :disabled="$nuxt.isOffline" @click.prevent="removeRecipeReferenceToList(recipe.id)">
<v-icon color="grey lighten-1">{{ $globals.icons.minus }}</v-icon>
</v-btn>
</v-list-item-action>
@@ -255,7 +251,7 @@
{{ shoppingList.recipeReferences[index].recipeQuantity }}
</div>
<v-list-item-action :key="'item-actions-increase' + recipe.id">
<v-btn icon :disabled="isOffline" @click.prevent="addRecipeReferenceToList(recipe.id)">
<v-btn icon :disabled="$nuxt.isOffline" @click.prevent="addRecipeReferenceToList(recipe.id)">
<v-icon color="grey lighten-1">{{ $globals.icons.createAlt }}</v-icon>
</v-btn>
</v-list-item-action>
@@ -268,7 +264,7 @@
<div class="d-flex justify-end">
<BaseButton
edit
:disabled="isOffline"
:disabled="$nuxt.isOffline"
@click="toggleSettingsDialog"
>
<template #icon> {{ $globals.icons.cog }} </template>
@@ -278,7 +274,7 @@
</v-lazy>
<v-lazy>
<div v-if="!isOffline" class="d-flex justify-end mt-10">
<div v-if="$nuxt.isOnline" class="d-flex justify-end mt-10">
<ButtonLink
:to="`/group/data/labels`"
:text="$tc('shopping-list.manage-labels')"
@@ -868,7 +864,6 @@ export default defineComponent({
// set a temporary updatedAt timestamp prior to refresh so it appears at the top of the checked items
item.updateAt = new Date().toISOString();
item.updateAt = item.updateAt.substring(0, item.updateAt.length-1);
}
// make updates reflect immediately
@@ -1073,7 +1068,6 @@ export default defineComponent({
getLabelColor,
groupSlug,
itemsByLabel,
isOffline: shoppingListItemActions.isOffline,
listItems,
loadingCounter,
preferences,

View File

@@ -51,7 +51,7 @@
<v-list-item-title>
{{ token.name }}
</v-list-item-title>
<v-list-item-subtitle> {{ $t('general.created-on-date', [$d(new Date(token.createdAt+"Z"))]) }} </v-list-item-subtitle>
<v-list-item-subtitle> {{ $t('general.created-on-date', [$d(new Date(token.createdAt))]) }} </v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<BaseButton delete small @click="deleteToken(token.id)"></BaseButton>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="mdi-calendar-multiselect" viewBox="0 0 24 24"><path d="M19,19V8H5V19H19M16,1H18V3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3H6V1H8V3H16V1M7,10H9V12H7V10M15,10H17V12H15V10M11,14H13V16H11V14M15,14H17V16H15V14Z" /></svg>

Before

Width:  |  Height:  |  Size: 298 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="mdi-format-list-checks" viewBox="0 0 24 24"><path d="M3,5H9V11H3V5M5,7V9H7V7H5M11,7H21V9H11V7M11,15H21V17H11V15M5,20L1.5,16.5L2.91,15.09L5,17.17L9.59,12.59L11,14L5,20Z" /></svg>

Before

Width:  |  Height:  |  Size: 222 B

View File

@@ -17,18 +17,14 @@ from mealie.services.scheduler import SchedulerRegistry, SchedulerService, tasks
settings = get_app_settings()
description = f"""
description = """
Mealie is a web application for managing your recipes, meal plans, and shopping lists. This is the Restful
API interactive documentation that can be used to explore the API. If you're justing getting started with
the API and want to get started quickly, you can use the
[API Usage | Mealie Docs](https://nightly.mealie.io/documentation/getting-started/api-usage/)
[API Usage | Mealie Docs](https://docs.mealie.io/documentation/getting-started/api-usage/)
as a reference for how to get started.
As of this release <b>{APP_VERSION}</b>, Mealie is still in rapid development and therefore some of these APIs may
change from version to version.
If you have any questions or comments about mealie, please use the discord server to talk to the developers or other
community members. If you'd like to file an issue, please use the
[GitHub Issue Tracker | Mealie](https://github.com/mealie-recipes/mealie/issues/new/choose)
@@ -36,10 +32,9 @@ community members. If you'd like to file an issue, please use the
## Helpful Links
- [Home Page](https://mealie.io)
- [Documentation](https://nightly.mealie.io)
- [Documentation](https://docs.mealie.io)
- [Discord](https://discord.gg/QuStdQGSGK)
- [Demo](https://demo.mealie.io)
- [Beta](https://demo.mealie.io)
"""
logger = get_logger()

View File

@@ -32,7 +32,7 @@ def get_latest_version() -> str:
global _LAST_RESET
now = datetime.datetime.now()
now = datetime.datetime.now(datetime.timezone.utc)
if not _LAST_RESET or now - _LAST_RESET > datetime.timedelta(days=MAX_DAYS_OLD):
_LAST_RESET = now

View File

@@ -38,7 +38,7 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
user = self.try_get_user(claims.get(settings.OIDC_USER_CLAIM))
is_admin = False
if settings.OIDC_USER_GROUP or settings.OIDC_ADMIN_GROUP:
group_claim = claims.get(settings.OIDC_GROUPS_CLAIM, [])
group_claim = claims.get(settings.OIDC_GROUPS_CLAIM, []) or []
is_admin = settings.OIDC_ADMIN_GROUP in group_claim if settings.OIDC_ADMIN_GROUP else False
is_valid_user = settings.OIDC_USER_GROUP in group_claim if settings.OIDC_USER_GROUP else True
@@ -82,7 +82,12 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
def get_claims(self, settings: AppSettings) -> JWTClaims | None:
"""Get the claims from the ID token and check if the required claims are present"""
required_claims = {"preferred_username", "name", "email", settings.OIDC_USER_CLAIM}
required_claims = {
"preferred_username",
"name",
"email",
settings.OIDC_USER_CLAIM,
}
jwks = OpenIDProvider.get_jwks(self.get_ttl_hash()) # cache the key set for 30 minutes
if not jwks:
return None

View File

@@ -72,9 +72,18 @@ class PostgresProvider(AbstractDBProvider, BaseSettings):
@property
def db_url_public(self) -> str:
user = self.POSTGRES_USER
password = self.POSTGRES_PASSWORD
return self.db_url.replace(user, "*****", 1).replace(password, "*****", 1)
if self.POSTGRES_URL_OVERRIDE:
return "Postgres Url Overridden"
return str(
PostgresDsn.build(
scheme="postgresql",
username="******",
password="******",
host=f"{self.POSTGRES_SERVER}:{self.POSTGRES_PORT}",
path=f"{self.POSTGRES_DB or ''}",
)
)
def db_provider_factory(provider_name: str, data_dir: Path, env_file: Path, env_encoding="utf-8") -> AbstractDBProvider:

View File

@@ -1,6 +1,10 @@
import logging
import secrets
from datetime import datetime, timezone
from pathlib import Path
from typing import NamedTuple
from dateutil.tz import tzlocal
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -9,6 +13,11 @@ from mealie.core.settings.themes import Theme
from .db_providers import AbstractDBProvider, db_provider_factory
class ScheduleTime(NamedTuple):
hour: int
minute: int
def determine_secrets(data_dir: Path, production: bool) -> str:
if not production:
return "shh-secret-test-key"
@@ -58,6 +67,44 @@ class AppSettings(BaseSettings):
ALLOW_SIGNUP: bool = False
DAILY_SCHEDULE_TIME: str = "23:45"
"""Local server time, in HH:MM format. See `DAILY_SCHEDULE_TIME_UTC` for the parsed UTC equivalent"""
_logger: logging.Logger | None = None
@property
def logger(self) -> logging.Logger:
if self._logger is None:
# lazy load the logger, since get_logger depends on the settings being loaded
from mealie.core.root_logger import get_logger
self._logger = get_logger()
return self._logger
@property
def DAILY_SCHEDULE_TIME_UTC(self) -> ScheduleTime:
"""The DAILY_SCHEDULE_TIME in UTC, parsed into hours and minutes"""
# parse DAILY_SCHEDULE_TIME into hours and minutes
try:
hour_str, minute_str = self.DAILY_SCHEDULE_TIME.split(":")
local_hour = int(hour_str)
local_minute = int(minute_str)
except ValueError:
local_hour = 23
local_minute = 45
self.logger.exception(
f"Unable to parse {self.DAILY_SCHEDULE_TIME=} as HH:MM; defaulting to {local_hour}:{local_minute}"
)
# DAILY_SCHEDULE_TIME is in local time, so we convert it to UTC
local_tz = tzlocal()
now = datetime.now(local_tz)
local_time = now.replace(hour=local_hour, minute=local_minute)
utc_time = local_time.astimezone(timezone.utc)
self.logger.debug(f"Local time: {local_hour}:{local_minute} | UTC time: {utc_time.hour}:{utc_time.minute}")
return ScheduleTime(utc_time.hour, utc_time.minute)
# ===============================================
# Security Configuration

View File

@@ -4,11 +4,13 @@ from sqlalchemy import DateTime, Integer
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from text_unidecode import unidecode
from ._model_utils.datetime import get_utc_now
class SqlAlchemyBase(DeclarativeBase):
id: Mapped[int] = mapped_column(Integer, primary_key=True)
created_at: Mapped[datetime | None] = mapped_column(DateTime, default=datetime.now, index=True)
update_at: Mapped[datetime | None] = mapped_column(DateTime, default=datetime.now, onupdate=datetime.now)
created_at: Mapped[datetime | None] = mapped_column(DateTime, default=get_utc_now, index=True)
update_at: Mapped[datetime | None] = mapped_column(DateTime, default=get_utc_now, onupdate=get_utc_now)
@classmethod
def normalize(cls, val: str) -> str:

View File

@@ -1,7 +0,0 @@
from .auto_init import auto_init
from .guid import GUID
__all__ = [
"auto_init",
"GUID",
]

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