diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 492df5395abe..0e909666aadc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -14,6 +14,7 @@ RUN echo "export PROMPT_COMMAND='history -a'" >> /home/vscode/.bashrc \ && echo "export HISTFILE=~/commandhistory/.bash_history" >> /home/vscode/.bashrc \ && chown vscode:vscode -R /home/vscode/ +RUN npm install -g @go-task/cli ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ @@ -37,4 +38,3 @@ RUN apt-get update \ libwebp-dev \ libsasl2-dev libldap2-dev libssl-dev \ gnupg gnupg2 gnupg1 -# && pip install -U --no-cache-dir pip diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 20f008b26677..a37cfd0076fd 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -46,7 +46,7 @@ ], // Use 'onCreateCommand' to run commands at the end of container creation. // Use 'postCreateCommand' to run commands after the container is created. - "onCreateCommand": "sudo chown -R vscode:vscode /workspaces/mealie/frontend/node_modules && make setup", + "onCreateCommand": "sudo chown -R vscode:vscode /workspaces/mealie/frontend/node_modules && task setup", // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode", // "features": { diff --git a/.github/DISCUSSION_TEMPLATE/feature-request.yaml b/.github/DISCUSSION_TEMPLATE/feature-request.yaml index 299ff50b29d2..25878a94c01d 100644 --- a/.github/DISCUSSION_TEMPLATE/feature-request.yaml +++ b/.github/DISCUSSION_TEMPLATE/feature-request.yaml @@ -11,7 +11,7 @@ body: options: - label: I used the GitHub search to find a similar requests and didn't find it. required: true - - label: Checked the [tasks tagged](https://github.com/hay-kot/mealie/issues?q=is%3Aissue+is%3Aopen+label%3Atask+) issues and verified my feature is not covered + - label: Checked the [tasks tagged](https://github.com/mealie-recipes/mealie/issues?q=is%3Aissue+is%3Aopen+label%3Atask+) issues and verified my feature is not covered required: true - type: textarea id: problem diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index f2810119acdd..9e7d1d1b4062 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -42,7 +42,7 @@ body: id: logs attributes: label: Please provide relevant logs - placeholder: For example from `docker-compose logs` or other system logs. + placeholder: For example from `docker compose logs` or other system logs. validations: required: true - type: textarea diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 4c6edf28d8f5..000000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,21 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 60 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security - - early-stages - - "bug: confirmed" - - feedback - - task -# Label to use when marking an issue as stale -staleLabel: stale -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000000..860a8ad31d2c --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,81 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "mealie-next" ] + pull_request: + branches: [ "mealie-next" ] + schedule: + - cron: '36 9 * * 3' + +jobs: + analyze: + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript-typescript', 'python' ] + # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] + # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/partial-backend.yml b/.github/workflows/partial-backend.yml index 154d98626b51..aefd386fabff 100644 --- a/.github/workflows/partial-backend.yml +++ b/.github/workflows/partial-backend.yml @@ -35,6 +35,12 @@ jobs: # Steps steps: + - name: Install Task + uses: arduino/setup-task@v1 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Check out repository uses: actions/checkout@v4 @@ -69,7 +75,7 @@ jobs: sudo apt-get update sudo apt-get install libsasl2-dev libldap2-dev libssl-dev poetry install - poetry add "psycopg2-binary==2.8.6" + poetry add "psycopg2-binary==2.9.9" if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' || steps.cache-validate.outputs.cache-hit-success != 'true' - name: Formatting (Black) @@ -78,11 +84,11 @@ jobs: - name: Lint (Ruff) run: | - make backend-lint + task py:lint - name: Mypy Typecheck run: | - make backend-typecheck + task py:mypy - name: Pytest env: @@ -101,4 +107,4 @@ jobs: LDAP_NAME_ATTRIBUTE: cn LDAP_MAIL_ATTRIBUTE: mail run: | - make backend-test + task py:test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b1b6fe6574e..78162ddcf6cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,4 +42,27 @@ jobs: DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }} uses: Ilshidur/action-discord@0.3.2 with: - args: "🚀 Version {{ EVENT_PAYLOAD.release.tag_name }} of Mealie has been released. See the release notes https://github.com/hay-kot/mealie/releases/tag/{{ EVENT_PAYLOAD.release.tag_name }}" + args: "🚀 Version {{ EVENT_PAYLOAD.release.tag_name }} of Mealie has been released. See the release notes https://github.com/mealie-recipes/mealie/releases/tag/{{ EVENT_PAYLOAD.release.tag_name }}" + + update-image-tags: + name: Update image tag in sample docker-compose files + needs: + - build-release + runs-on: ubuntu-latest + steps: + - name: Checkout 🛎 + uses: actions/checkout@v4 + + - 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 + + - name: Commit updates + uses: test-room-7/action-update-file@v1 + with: + file-path: | + docs/docs/documentation/getting-started/installation/sqlite.md + docs/docs/documentation/getting-started/installation/postgres.md + commit-msg: "Change image tag, for release ${{ github.event.release.tag_name }}" + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000000..c1443c1d4ad7 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,41 @@ +# Documentation: https://github.com/marketplace/actions/close-stale-issues +name: 'Stale: Flag and close stale issues and PRs' +on: + schedule: + - cron: '30 1 * * *' + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + stale-issue-label: 'stale' + exempt-issue-labels: 'pinned,security,early-stages,bug: confirmed,feedback,task' + stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.' + days-before-issue-stale: 30 + days-before-issue-close: 5 + stale-pr-label: 'stale' + stale-pr-message: 'This PR is stale because it has been open 45 days with no activity.' + days-before-pr-stale: 45 + # This stops a PR from ever getting closed automatically. + days-before-pr-close: -1 + # If an issue/PR has a milestone, it's exempt from being marked as stale. + exempt-all-milestones: true + # How many API calls will we allow the action to make, essentially. + # Doco: https://github.com/actions/stale?tab=readme-ov-file#operations-per-run + operations-per-run: 150 + ######################################################################## + # The below are just default values, but populating here for reference # + ######################################################################## + # Automatically remove the stale label when the issues or the pull requests are updated + remove-stale-when-updated: true + # The reason used when closing issues. Valid values are `completed` and `not_planned`. + close-issue-reason: 'not_planned' + # If true, PRs currently in draft will not be marked as stale automatically. + # We can mark them stale (after `days-before-pr-stale`), though we don't auto-close. + exempt-draft-pr: false diff --git a/.gitignore b/.gitignore index 1259a442359c..f853ecb97f27 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,4 @@ lcov.info dev/code-generation/openapi.json .run/ +.task/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0a109f865579..aafe1be8e1ca 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,6 +11,6 @@ repos: - id: trailing-whitespace exclude: ^tests/data/ - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 24.1.0 hooks: - id: black diff --git a/.pylintrc b/.pylintrc index 4d6de19720bc..05cbd74d41ee 100644 --- a/.pylintrc +++ b/.pylintrc @@ -60,17 +60,7 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=print-statement, - parameter-unpacking, - unpacking-in-except, - old-raise-syntax, - backtick, - long-suffix, - old-ne-operator, - old-octal-literal, - import-star-module-level, - non-ascii-bytes-literal, - raw-checker-failed, +disable=raw-checker-failed, bad-inline-option, locally-disabled, file-ignored, @@ -78,67 +68,10 @@ disable=print-statement, useless-suppression, deprecated-pragma, use-symbolic-message-instead, - apply-builtin, - basestring-builtin, - buffer-builtin, - cmp-builtin, - coerce-builtin, - execfile-builtin, - file-builtin, - long-builtin, - raw_input-builtin, - reduce-builtin, - standarderror-builtin, - unicode-builtin, - xrange-builtin, - coerce-method, - delslice-method, - getslice-method, - setslice-method, - no-absolute-import, - old-division, - dict-iter-method, - dict-view-method, - next-method-called, - metaclass-assignment, - indexing-exception, - raising-string, - reload-builtin, - oct-method, - hex-method, - nonzero-method, - cmp-method, - input-builtin, - round-builtin, - intern-builtin, - unichr-builtin, - map-builtin-not-iterating, - zip-builtin-not-iterating, - range-builtin-not-iterating, - filter-builtin-not-iterating, - using-cmp-argument, - eq-without-hash, - div-method, - idiv-method, - rdiv-method, - exception-message-attribute, - invalid-str-codec, - sys-max-int, - bad-python3-import, - deprecated-string-function, - deprecated-str-translate-call, - deprecated-itertools-function, - deprecated-types-field, - next-method-defined, - dict-items-not-iterating, - dict-keys-not-iterating, - dict-values-not-iterating, - deprecated-operator-function, - deprecated-urllib-function, - xreadlines-attribute, - deprecated-sys-function, - exception-escape, - comprehension-escape + missing-function-docstring, + missing-class-docstring, + missing-module-docstring, + too-few-public-methods # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -292,7 +225,9 @@ good-names=i, k, ex, Run, - _ + _, + e, + db # Good variable names regexes, separated by a comma. If names match any regex, # they will always be accepted @@ -455,7 +390,7 @@ indent-after-paren=4 indent-string=' ' # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=120 # Maximum number of lines in a module. max-module-lines=1000 @@ -583,5 +518,5 @@ valid-metaclass-classmethod-first-arg=cls # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception +overgeneral-exceptions=builtins.BaseException, + builtins.Exception diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 8359af04b5d8..f73e066457a6 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,22 +1,9 @@ { "version": "2.0.0", "tasks": [ - { - "label": "DEV: Build and Start Docker Compose", - "command": "make docker-dev", - "type": "shell", - "args": [], - "problemMatcher": [ - "$tsc" - ], - "presentation": { - "reveal": "always" - }, - "group": "test" - }, { "label": "Production: Build and Start Docker Compose", - "command": "make docker-prod", + "command": "task docker:prod", "type": "shell", "args": [], "problemMatcher": [ @@ -29,7 +16,7 @@ }, { "label": "Dev: Start Backend", - "command": "make backend", + "command": "task py", "type": "shell", "presentation": { "reveal": "always", @@ -49,7 +36,7 @@ }, { "label": "Dev: Start Frontend", - "command": "make frontend", + "command": "task ui", "type": "shell", "presentation": { "reveal": "always", @@ -59,7 +46,7 @@ }, { "label": "Dev: Start Docs Server", - "command": "make docs", + "command": "task docs", "type": "shell", "presentation": { "reveal": "always", @@ -69,7 +56,7 @@ }, { "label": "Run python tests", - "command": "make test", + "command": "task py:test", "type": "shell", "presentation": { "reveal": "always" diff --git a/README.md b/README.md index 983aafc549a6..c6a2ada2c07c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ +[![Latest Release][latest-release-shield]][latest-release-url] [![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] [![AGPL License][license-shield]][license-url] [![Docker Pulls][docker-pull]][docker-pull] -[![CodeFactor](https://www.codefactor.io/repository/github/mealie-recipes/mealie/badge)](https://www.codefactor.io/repository/github/mealie-recipes/mealie)
@@ -91,6 +91,8 @@ Thanks to Linode for providing Hosting for the Demo, Beta, and Documentation sit [stars-url]: https://github.com/mealie-recipes/mealie/stargazers [issues-shield]: https://img.shields.io/github/issues/mealie-recipes/mealie.svg?style=flat-square [issues-url]: https://github.com/mealie-recipes/mealie/issues +[latest-release-shield]: https://img.shields.io/github/v/release/mealie-recipes/mealie?style=flat-square&label=latest%20release +[latest-release-url]: https://img.shields.io/github/v/release/mealie-recipes/mealie [license-shield]: https://img.shields.io/github/license/mealie-recipes/mealie.svg?style=flat-square [license-url]: https://github.com/mealie-recipes/mealie/blob/mealie-next/LICENSE [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555 diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 000000000000..ed3f47d015b4 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,183 @@ +# https://taskfile.dev + +version: "3" +vars: + GREETING: Hello, World! +env: + DEFAULT_GROUP: Home + PRODUCTION: false + API_PORT: 9000 + API_DOCS: True + TOKEN_TIME: 256 # hours + # mailplit SMTP config + # start dev:services to use mailpit + SMTP_HOST: localhost + SMTP_PORT: 1025 + SMTP_FROM_NAME: MealieDev + SMTP_AUTH_STRATEGY: NONE + LANG: en-US + +# loads .env file if it exists +dotenv: + - .env + - .dev.env +tasks: + docs:gen: + desc: runs the API documentation generator + cmds: + - poetry run python dev/code-generation/gen_docs_api.py + + docs: + desc: runs the documentation server + dir: docs + deps: + - docs:gen + cmds: + - poetry run python -m mkdocs serve + + setup:ui: + desc: setup frontend dependencies + dir: frontend + cmds: + - yarn install + + setup:py: + desc: setup python dependencies + cmds: + - poetry install --with main,dev,postgres + - poetry run pre-commit install + + setup:model: + desc: setup nlp model + vars: + MODEL_URL: https://github.com/mealie-recipes/nlp-model/releases/download/v1.0.0/model.crfmodel + OUTPUT: ./mealie/services/parser_services/crfpp/model.crfmodel + sources: + # using pyproject.toml as the dependency since this should only ever need to run once + # during setup. There is perhaps a better way to do this. + - ./pyproject.toml + generates: + - ./mealie/services/parser_services/crfpp/model.crfmodel + cmds: + - curl -L0 {{ .MODEL_URL }} --output {{ .OUTPUT }} + + setup: + desc: setup all dependencies + deps: + - setup:ui + - setup:py + - setup:model + + dev:generate: + desc: run code generators + cmds: + - poetry run python dev/code-generation/main.py + + dev:services: + desc: starts postgres and mailpit containers + dir: docker + cmds: + - docker compose -f docker-compose.dev.yml up + + dev:clean: + desc: cleans up dev environment !! removes all data files !! + vars: + DEV_DATA: "" + cmds: + - rm -r ./dev/data/recipes/ + - rm -r ./dev/data/users/ + - rm -f ./dev/data/mealie*.db + - rm -f ./dev/data/mealie.log + - rm -f ./dev/data/.secret + + py:mypy: + desc: runs python type checking + cmds: + - poetry run mypy mealie + + py:test: + desc: runs python tests (support args after '--') + cmds: + - poetry run pytest {{ .CLI_ARGS }} + + py:format: + desc: runs python code formatter + cmds: + - poetry run black mealie + + py:lint: + desc: runs python linter + cmds: + - poetry run ruff mealie + + py:check: + desc: runs all linters, type checkers, and formatters + deps: + - py:format + - py:lint + - py:mypy + - py:test + + py:coverage: + desc: runs python coverage and generates html report + cmds: + - poetry run pytest + - poetry run coverage report -m + - poetry run coveragepy-lcov + - poetry run coverage html + - open htmlcov/index.html + + py: + desc: runs the backend server + cmds: + - poetry run python mealie/db/init_db.py + - poetry run python mealie/app.py + + py:postgres: + desc: runs the backend server configured for containerized postgres + env: + DB_ENGINE: postgres + POSTGRES_USER: mealie + POSTGRES_PASSWORD: mealie + POSTGRES_SERVER: localhost + POSTGRES_PORT: 5432 + POSTGRES_DB: mealie + cmds: + - poetry run python mealie/db/init_db.py + - poetry run python mealie/app.py + + ui:build: + desc: builds the frontend in frontend/dist + dir: frontend + cmds: + - yarn build + + ui:lint: + desc: runs the frontend linter + dir: frontend + cmds: + - yarn lint + + ui:test: + desc: runs the frontend tests + dir: frontend + cmds: + - yarn test + + ui:check: + desc: runs all frontend checks + deps: + - ui:lint + - ui:test + + ui: + desc: runs the frontend server + dir: frontend + cmds: + - yarn run dev + + docker:prod: + desc: builds and runs the production docker image locally + dir: docker + cmds: + - docker compose -f docker-compose.yml -p mealie up -d --build diff --git a/alembic/versions/2022-02-21-19.56.24_6b0f5f32d602_initial_tables.py b/alembic/versions/2022-02-21-19.56.24_6b0f5f32d602_initial_tables.py index 03cd0918bda8..8ebd1b395c12 100644 --- a/alembic/versions/2022-02-21-19.56.24_6b0f5f32d602_initial_tables.py +++ b/alembic/versions/2022-02-21-19.56.24_6b0f5f32d602_initial_tables.py @@ -1,10 +1,11 @@ """Initial tables Revision ID: 6b0f5f32d602 -Revises: +Revises: Create Date: 2022-02-21 19:56:24.351115 """ + import sqlalchemy as sa from sqlalchemy import engine_from_config diff --git a/alembic/versions/2022-03-23-17.43.34_263dd6707191_convert_quantity_from_integer_to_float.py b/alembic/versions/2022-03-23-17.43.34_263dd6707191_convert_quantity_from_integer_to_float.py index b453ecdff04c..b9de7e23d69f 100644 --- a/alembic/versions/2022-03-23-17.43.34_263dd6707191_convert_quantity_from_integer_to_float.py +++ b/alembic/versions/2022-03-23-17.43.34_263dd6707191_convert_quantity_from_integer_to_float.py @@ -5,6 +5,7 @@ Revises: 6b0f5f32d602 Create Date: 2022-03-23 17:43:34.727829 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2022-03-27-19.30.28_f1a2dbee5fe9_add_original_text_column_to_recipes_.py b/alembic/versions/2022-03-27-19.30.28_f1a2dbee5fe9_add_original_text_column_to_recipes_.py index 62698ded844c..fc62f3517e4d 100644 --- a/alembic/versions/2022-03-27-19.30.28_f1a2dbee5fe9_add_original_text_column_to_recipes_.py +++ b/alembic/versions/2022-03-27-19.30.28_f1a2dbee5fe9_add_original_text_column_to_recipes_.py @@ -5,6 +5,7 @@ Revises: 263dd6707191 Create Date: 2022-03-27 19:30:28.545846 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2022-03-31-19.19.55_59eb59135381_add_tags_to_cookbooks.py b/alembic/versions/2022-03-31-19.19.55_59eb59135381_add_tags_to_cookbooks.py index c0c99246cd49..ed751d9df75e 100644 --- a/alembic/versions/2022-03-31-19.19.55_59eb59135381_add_tags_to_cookbooks.py +++ b/alembic/versions/2022-03-31-19.19.55_59eb59135381_add_tags_to_cookbooks.py @@ -5,6 +5,7 @@ Revises: f1a2dbee5fe9 Create Date: 2022-03-31 19:19:55.428965 """ + import sqlalchemy as sa import mealie.db.migration_types diff --git a/alembic/versions/2022-04-03-10.48.51_09dfc897ad62_add_require_all_for_cookbook_filters.py b/alembic/versions/2022-04-03-10.48.51_09dfc897ad62_add_require_all_for_cookbook_filters.py index f58414d29b5e..19cd08c855c7 100644 --- a/alembic/versions/2022-04-03-10.48.51_09dfc897ad62_add_require_all_for_cookbook_filters.py +++ b/alembic/versions/2022-04-03-10.48.51_09dfc897ad62_add_require_all_for_cookbook_filters.py @@ -5,6 +5,7 @@ Revises: 59eb59135381 Create Date: 2022-04-03 10:48:51.379968 """ + import sqlalchemy as sa import mealie.db.migration_types # noqa: F401 diff --git a/alembic/versions/2022-06-01-11.12.06_ab0bae02578f_add_use_abbreviation_column_to_.py b/alembic/versions/2022-06-01-11.12.06_ab0bae02578f_add_use_abbreviation_column_to_.py index 3f39ef0ddcba..ed46e40b67e1 100644 --- a/alembic/versions/2022-06-01-11.12.06_ab0bae02578f_add_use_abbreviation_column_to_.py +++ b/alembic/versions/2022-06-01-11.12.06_ab0bae02578f_add_use_abbreviation_column_to_.py @@ -5,6 +5,7 @@ Revises: 09dfc897ad62 Create Date: 2022-06-01 11:12:06.748383 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2022-06-15-21.05.34_f30cf048c228_add_new_webhook_fields.py b/alembic/versions/2022-06-15-21.05.34_f30cf048c228_add_new_webhook_fields.py index 5b1d029a1721..085b9b29c4db 100644 --- a/alembic/versions/2022-06-15-21.05.34_f30cf048c228_add_new_webhook_fields.py +++ b/alembic/versions/2022-06-15-21.05.34_f30cf048c228_add_new_webhook_fields.py @@ -6,6 +6,7 @@ Revises: ab0bae02578f Create Date: 2022-06-15 21:05:34.851857 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2022-08-12-19.05.59_188374910655_add_login_attemps_and_locked_at_field_.py b/alembic/versions/2022-08-12-19.05.59_188374910655_add_login_attemps_and_locked_at_field_.py index 9db5b91b1da8..45fedc026163 100644 --- a/alembic/versions/2022-08-12-19.05.59_188374910655_add_login_attemps_and_locked_at_field_.py +++ b/alembic/versions/2022-08-12-19.05.59_188374910655_add_login_attemps_and_locked_at_field_.py @@ -5,6 +5,7 @@ Revises: f30cf048c228 Create Date: 2022-08-12 19:05:59.776361 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2022-08-13-17.07.07_089bfa50d0ed_add_is_ocr_recipe_column_to_recipes.py b/alembic/versions/2022-08-13-17.07.07_089bfa50d0ed_add_is_ocr_recipe_column_to_recipes.py index 51da2f319d5f..ba1595486e81 100644 --- a/alembic/versions/2022-08-13-17.07.07_089bfa50d0ed_add_is_ocr_recipe_column_to_recipes.py +++ b/alembic/versions/2022-08-13-17.07.07_089bfa50d0ed_add_is_ocr_recipe_column_to_recipes.py @@ -5,6 +5,7 @@ Revises: 188374910655 Create Date: 2022-08-05 17:07:07.389271 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2022-08-29-13.57.40_44e8d670719d_add_extras_to_shopping_lists_list_items_.py b/alembic/versions/2022-08-29-13.57.40_44e8d670719d_add_extras_to_shopping_lists_list_items_.py index 8a5477a8d858..c688c0e603af 100644 --- a/alembic/versions/2022-08-29-13.57.40_44e8d670719d_add_extras_to_shopping_lists_list_items_.py +++ b/alembic/versions/2022-08-29-13.57.40_44e8d670719d_add_extras_to_shopping_lists_list_items_.py @@ -5,6 +5,7 @@ Revises: 089bfa50d0ed Create Date: 2022-08-29 13:57:40.452245 """ + import sqlalchemy as sa import mealie.db.migration_types diff --git a/alembic/versions/2022-09-27-14.53.14_2ea7a807915c_add_recipe_timeline_events_table.py b/alembic/versions/2022-09-27-14.53.14_2ea7a807915c_add_recipe_timeline_events_table.py index 1228bd06bf06..57edfe315b23 100644 --- a/alembic/versions/2022-09-27-14.53.14_2ea7a807915c_add_recipe_timeline_events_table.py +++ b/alembic/versions/2022-09-27-14.53.14_2ea7a807915c_add_recipe_timeline_events_table.py @@ -5,6 +5,7 @@ Revises: 44e8d670719d Create Date: 2022-09-27 14:53:14.111054 """ + import sqlalchemy as sa import mealie.db.migration_types diff --git a/alembic/versions/2022-11-03-13.10.24_1923519381ad_renamed_timeline_event_message_and_.py b/alembic/versions/2022-11-03-13.10.24_1923519381ad_renamed_timeline_event_message_and_.py index 761f3d7e7463..e68c5bcf8738 100644 --- a/alembic/versions/2022-11-03-13.10.24_1923519381ad_renamed_timeline_event_message_and_.py +++ b/alembic/versions/2022-11-03-13.10.24_1923519381ad_renamed_timeline_event_message_and_.py @@ -5,6 +5,7 @@ Revises: 2ea7a807915c Create Date: 2022-11-03 13:10:24.811134 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2022-11-22-03.42.45_167eb69066ad_add_recipe_scale_to_shopping_list_item_.py b/alembic/versions/2022-11-22-03.42.45_167eb69066ad_add_recipe_scale_to_shopping_list_item_.py index 42f508cd3673..dc83ca47d501 100644 --- a/alembic/versions/2022-11-22-03.42.45_167eb69066ad_add_recipe_scale_to_shopping_list_item_.py +++ b/alembic/versions/2022-11-22-03.42.45_167eb69066ad_add_recipe_scale_to_shopping_list_item_.py @@ -5,6 +5,7 @@ Revises: 1923519381ad Create Date: 2022-11-22 03:42:45.494567 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2023-01-21-16.54.44_165d943c64ee_add_related_user_to_mealplan.py b/alembic/versions/2023-01-21-16.54.44_165d943c64ee_add_related_user_to_mealplan.py index c9e511b1d337..103039563431 100644 --- a/alembic/versions/2023-01-21-16.54.44_165d943c64ee_add_related_user_to_mealplan.py +++ b/alembic/versions/2023-01-21-16.54.44_165d943c64ee_add_related_user_to_mealplan.py @@ -5,6 +5,7 @@ Revises: 167eb69066ad Create Date: 2023-01-21 16:54:44.368768 """ + import sqlalchemy as sa import mealie.db.migration_types diff --git a/alembic/versions/2023-02-10-21.18.32_16160bf731a0_add_more_indices_necessary_for_search.py b/alembic/versions/2023-02-10-21.18.32_16160bf731a0_add_more_indices_necessary_for_search.py index 29268c6e3b7d..22536e6a4e30 100644 --- a/alembic/versions/2023-02-10-21.18.32_16160bf731a0_add_more_indices_necessary_for_search.py +++ b/alembic/versions/2023-02-10-21.18.32_16160bf731a0_add_more_indices_necessary_for_search.py @@ -5,6 +5,7 @@ Revises: ff5f73b01a7a Create Date: 2023-02-10 21:18:32.405130 """ + import sqlalchemy as sa import mealie.db.migration_types diff --git a/alembic/versions/2023-02-14-20.45.41_5ab195a474eb_add_normalized_search_properties.py b/alembic/versions/2023-02-14-20.45.41_5ab195a474eb_add_normalized_search_properties.py index 773aaf5c1212..ea638febd56b 100644 --- a/alembic/versions/2023-02-14-20.45.41_5ab195a474eb_add_normalized_search_properties.py +++ b/alembic/versions/2023-02-14-20.45.41_5ab195a474eb_add_normalized_search_properties.py @@ -5,6 +5,7 @@ Revises: 16160bf731a0 Create Date: 2023-02-14 20:45:41.102571 """ + import sqlalchemy as sa from sqlalchemy import orm, select from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column diff --git a/alembic/versions/2023-02-21-22.03.19_b04a08da2108_added_shopping_list_label_settings.py b/alembic/versions/2023-02-21-22.03.19_b04a08da2108_added_shopping_list_label_settings.py index edca93c2b6a4..a230433292a7 100644 --- a/alembic/versions/2023-02-21-22.03.19_b04a08da2108_added_shopping_list_label_settings.py +++ b/alembic/versions/2023-02-21-22.03.19_b04a08da2108_added_shopping_list_label_settings.py @@ -5,6 +5,7 @@ Revises: 5ab195a474eb Create Date: 2023-21-02 22:03:19.837244 """ + from uuid import uuid4 import sqlalchemy as sa diff --git a/alembic/versions/2023-02-22-21.45.52_38514b39a824_add_auth_method_to_user_table.py b/alembic/versions/2023-02-22-21.45.52_38514b39a824_add_auth_method_to_user_table.py index 95d92086539c..83103cf27bf9 100644 --- a/alembic/versions/2023-02-22-21.45.52_38514b39a824_add_auth_method_to_user_table.py +++ b/alembic/versions/2023-02-22-21.45.52_38514b39a824_add_auth_method_to_user_table.py @@ -5,6 +5,7 @@ Revises: b04a08da2108 Create Date: 2023-02-22 21:45:52.900964 """ + import sqlalchemy as sa import mealie.db.migration_types diff --git a/alembic/versions/2023-04-13-06.47.04_b3dbb554ba53_postgres_fuzzy_search.py b/alembic/versions/2023-04-13-06.47.04_b3dbb554ba53_postgres_fuzzy_search.py index 159087bc86e8..3f4a2543051a 100644 --- a/alembic/versions/2023-04-13-06.47.04_b3dbb554ba53_postgres_fuzzy_search.py +++ b/alembic/versions/2023-04-13-06.47.04_b3dbb554ba53_postgres_fuzzy_search.py @@ -5,6 +5,7 @@ Revises: 38514b39a824 Create Date: 2023-04-13 06:47:04.617131 """ + import sqlalchemy as sa import mealie.db.migration_types diff --git a/alembic/versions/2023-08-06-21.00.34_04ac51cbe9a4_added_group_slug.py b/alembic/versions/2023-08-06-21.00.34_04ac51cbe9a4_added_group_slug.py index be341fde57f4..50748f94276d 100644 --- a/alembic/versions/2023-08-06-21.00.34_04ac51cbe9a4_added_group_slug.py +++ b/alembic/versions/2023-08-06-21.00.34_04ac51cbe9a4_added_group_slug.py @@ -5,6 +5,7 @@ Revises: b3dbb554ba53 Create Date: 2023-08-06 21:00:34.582905 """ + import sqlalchemy as sa from slugify import slugify from sqlalchemy.orm import Session diff --git a/alembic/versions/2023-08-14-19.30.49_1825b5225403_added_recipe_note_to_shopping_list_.py b/alembic/versions/2023-08-14-19.30.49_1825b5225403_added_recipe_note_to_shopping_list_.py index b6a6489016b3..a095a8a5b8b5 100644 --- a/alembic/versions/2023-08-14-19.30.49_1825b5225403_added_recipe_note_to_shopping_list_.py +++ b/alembic/versions/2023-08-14-19.30.49_1825b5225403_added_recipe_note_to_shopping_list_.py @@ -5,6 +5,7 @@ Revises: 04ac51cbe9a4 Create Date: 2023-08-14 19:30:49.103185 """ + import sqlalchemy as sa from alembic import op diff --git a/alembic/versions/2023-08-15-16.25.07_bcfdad6b7355_remove_tool_name_and_slug_unique_.py b/alembic/versions/2023-08-15-16.25.07_bcfdad6b7355_remove_tool_name_and_slug_unique_.py index c61e0b989436..822202d69596 100644 --- a/alembic/versions/2023-08-15-16.25.07_bcfdad6b7355_remove_tool_name_and_slug_unique_.py +++ b/alembic/versions/2023-08-15-16.25.07_bcfdad6b7355_remove_tool_name_and_slug_unique_.py @@ -5,6 +5,7 @@ Revises: 1825b5225403 Create Date: 2023-08-15 16:25:07.058929 """ + from alembic import op # revision identifiers, used by Alembic. diff --git a/alembic/versions/2023-09-01-14.55.42_0341b154f79a_added_normalized_unit_and_food_names.py b/alembic/versions/2023-09-01-14.55.42_0341b154f79a_added_normalized_unit_and_food_names.py index f77fe6fbc21e..1cbf60a7ec88 100644 --- a/alembic/versions/2023-09-01-14.55.42_0341b154f79a_added_normalized_unit_and_food_names.py +++ b/alembic/versions/2023-09-01-14.55.42_0341b154f79a_added_normalized_unit_and_food_names.py @@ -5,6 +5,7 @@ Revises: bcfdad6b7355 Create Date: 2023-09-01 14:55:42.166766 """ + import sqlalchemy as sa from sqlalchemy import orm, select diff --git a/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py b/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py index 885e619f8199..57a59a077125 100644 --- a/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py +++ b/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py @@ -5,6 +5,7 @@ Revises: 0341b154f79a Create Date: 2023-10-04 14:29:26.688065 """ + from collections import defaultdict from dataclasses import dataclass from typing import Any @@ -58,7 +59,12 @@ def _resolve_duplicate_food( keep_food_id: UUID4, dupe_food_id: UUID4, ): - for shopping_list_item in session.query(ShoppingListItem).filter_by(food_id=dupe_food_id).all(): + for shopping_list_item in ( + session.query(ShoppingListItem) + .options(load_only(ShoppingListItem.id, ShoppingListItem.food_id)) + .filter_by(food_id=dupe_food_id) + .all() + ): shopping_list_item.food_id = keep_food_id for recipe_ingredient in ( @@ -69,9 +75,11 @@ def _resolve_duplicate_food( ): recipe_ingredient.food_id = keep_food_id + session.commit() session.execute( sa.text(f"DELETE FROM {IngredientFoodModel.__tablename__} WHERE id=:id").bindparams(id=dupe_food_id) ) + session.commit() def _resolve_duplicate_unit( @@ -79,15 +87,27 @@ def _resolve_duplicate_unit( keep_unit_id: UUID4, dupe_unit_id: UUID4, ): - for shopping_list_item in session.query(ShoppingListItem).filter_by(unit_id=dupe_unit_id).all(): + for shopping_list_item in ( + session.query(ShoppingListItem) + .options(load_only(ShoppingListItem.id, ShoppingListItem.unit_id)) + .filter_by(unit_id=dupe_unit_id) + .all() + ): shopping_list_item.unit_id = keep_unit_id - for recipe_ingredient in session.query(RecipeIngredientModel).filter_by(unit_id=dupe_unit_id).all(): + for recipe_ingredient in ( + session.query(RecipeIngredientModel) + .options(load_only(RecipeIngredientModel.id, RecipeIngredientModel.unit_id)) + .filter_by(unit_id=dupe_unit_id) + .all() + ): recipe_ingredient.unit_id = keep_unit_id + session.commit() session.execute( sa.text(f"DELETE FROM {IngredientUnitModel.__tablename__} WHERE id=:id").bindparams(id=dupe_unit_id) ) + session.commit() def _resolve_duplicate_label( @@ -95,13 +115,25 @@ def _resolve_duplicate_label( keep_label_id: UUID4, dupe_label_id: UUID4, ): - for shopping_list_item in session.query(ShoppingListItem).filter_by(label_id=dupe_label_id).all(): + for shopping_list_item in ( + session.query(ShoppingListItem) + .options(load_only(ShoppingListItem.id, ShoppingListItem.label_id)) + .filter_by(label_id=dupe_label_id) + .all() + ): shopping_list_item.label_id = keep_label_id - for ingredient_food in session.query(IngredientFoodModel).filter_by(label_id=dupe_label_id).all(): + for ingredient_food in ( + session.query(IngredientFoodModel) + .options(load_only(IngredientFoodModel.id, IngredientFoodModel.label_id)) + .filter_by(label_id=dupe_label_id) + .all() + ): ingredient_food.label_id = keep_label_id + session.commit() session.execute(sa.text(f"DELETE FROM {MultiPurposeLabel.__tablename__} WHERE id=:id").bindparams(id=dupe_label_id)) + session.commit() def _resolve_duplicate_foods_units_labels(session: Session): @@ -140,6 +172,7 @@ def _remove_duplicates_from_m2m_table(session: Session, table_meta: TableMeta): ) session.execute(query) + session.commit() def _remove_duplicates_from_m2m_tables(session: Session, table_metas: list[TableMeta]): diff --git a/alembic/versions/2023-10-19-19.22.55_ba1e4a6cfe99_added_plural_names_and_alias_tables_for_.py b/alembic/versions/2023-10-19-19.22.55_ba1e4a6cfe99_added_plural_names_and_alias_tables_for_.py index 23952dc704a0..d7d3762f45b2 100644 --- a/alembic/versions/2023-10-19-19.22.55_ba1e4a6cfe99_added_plural_names_and_alias_tables_for_.py +++ b/alembic/versions/2023-10-19-19.22.55_ba1e4a6cfe99_added_plural_names_and_alias_tables_for_.py @@ -5,6 +5,7 @@ Revises: dded3119c1fe Create Date: 2023-10-19 19:22:55.369319 """ + import sqlalchemy as sa import mealie.db.migration_types diff --git a/dev/code-generation/gen_ts_locales.py b/dev/code-generation/gen_ts_locales.py index 451918c3038e..397d2b79a495 100644 --- a/dev/code-generation/gen_ts_locales.py +++ b/dev/code-generation/gen_ts_locales.py @@ -1,4 +1,5 @@ import pathlib +from dataclasses import dataclass from pathlib import Path import dotenv @@ -15,38 +16,44 @@ BASE = pathlib.Path(__file__).parent.parent.parent API_KEY = dotenv.get_key(BASE / ".env", "CROWDIN_API_KEY") -NAMES = { - "en-US": "American English", - "en-GB": "British English", - "af-ZA": "Afrikaans (Afrikaans)", - "ar-SA": "العربية (Arabic)", - "ca-ES": "Català (Catalan)", - "cs-CZ": "Čeština (Czech)", - "da-DK": "Dansk (Danish)", - "de-DE": "Deutsch (German)", - "el-GR": "Ελληνικά (Greek)", - "es-ES": "Español (Spanish)", - "fi-FI": "Suomi (Finnish)", - "fr-FR": "Français (French)", - "he-IL": "עברית (Hebrew)", - "hu-HU": "Magyar (Hungarian)", - "it-IT": "Italiano (Italian)", - "ja-JP": "日本語 (Japanese)", - "ko-KR": "한국어 (Korean)", - "no-NO": "Norsk (Norwegian)", - "nl-NL": "Nederlands (Dutch)", - "pl-PL": "Polski (Polish)", - "pt-BR": "Português do Brasil (Brazilian Portuguese)", - "pt-PT": "Português (Portuguese)", - "ro-RO": "Română (Romanian)", - "ru-RU": "Pусский (Russian)", - "sr-SP": "српски (Serbian)", - "sv-SE": "Svenska (Swedish)", - "tr-TR": "Türkçe (Turkish)", - "uk-UA": "Українська (Ukrainian)", - "vi-VN": "Tiếng Việt (Vietnamese)", - "zh-CN": "简体中文 (Chinese simplified)", - "zh-TW": "繁體中文 (Chinese traditional)", +@dataclass +class LocaleData: + name: str + dir: str = "ltr" + + +LOCALE_DATA: dict[str, LocaleData] = { + "en-US": LocaleData(name="American English"), + "en-GB": LocaleData(name="British English"), + "af-ZA": LocaleData(name="Afrikaans (Afrikaans)"), + "ar-SA": LocaleData(name="العربية (Arabic)", dir="rtl"), + "ca-ES": LocaleData(name="Català (Catalan)"), + "cs-CZ": LocaleData(name="Čeština (Czech)"), + "da-DK": LocaleData(name="Dansk (Danish)"), + "de-DE": LocaleData(name="Deutsch (German)"), + "el-GR": LocaleData(name="Ελληνικά (Greek)"), + "es-ES": LocaleData(name="Español (Spanish)"), + "fi-FI": LocaleData(name="Suomi (Finnish)"), + "fr-FR": LocaleData(name="Français (French)"), + "he-IL": LocaleData(name="עברית (Hebrew)", dir="rtl"), + "hu-HU": LocaleData(name="Magyar (Hungarian)"), + "it-IT": LocaleData(name="Italiano (Italian)"), + "ja-JP": LocaleData(name="日本語 (Japanese)"), + "ko-KR": LocaleData(name="한국어 (Korean)"), + "no-NO": LocaleData(name="Norsk (Norwegian)"), + "nl-NL": LocaleData(name="Nederlands (Dutch)"), + "pl-PL": LocaleData(name="Polski (Polish)"), + "pt-BR": LocaleData(name="Português do Brasil (Brazilian Portuguese)"), + "pt-PT": LocaleData(name="Português (Portuguese)"), + "ro-RO": LocaleData(name="Română (Romanian)"), + "ru-RU": LocaleData(name="Pусский (Russian)"), + "sr-SP": LocaleData(name="српски (Serbian)"), + "sv-SE": LocaleData(name="Svenska (Swedish)"), + "tr-TR": LocaleData(name="Türkçe (Turkish)"), + "uk-UA": LocaleData(name="Українська (Ukrainian)"), + "vi-VN": LocaleData(name="Tiếng Việt (Vietnamese)"), + "zh-CN": LocaleData(name="简体中文 (Chinese simplified)"), + "zh-TW": LocaleData(name="繁體中文 (Chinese traditional)"), } LOCALE_TEMPLATE = """// This Code is auto generated by gen_global_components.py @@ -55,6 +62,7 @@ export const LOCALES = [{% for locale in locales %} name: "{{ locale.name }}", value: "{{ locale.locale }}", progress: {{ locale.progress }}, + dir: "{{ locale.dir }}", },{% endfor %} ] @@ -65,12 +73,14 @@ class TargetLanguage(MealieModel): id: str name: str locale: str + dir: str = "ltr" threeLettersCode: str twoLettersCode: str progress: float = 0.0 class Config: extra = Extra.allow + allow_population_by_field_name = True class CrowdinApi: @@ -103,15 +113,23 @@ class CrowdinApi: models.insert( 0, TargetLanguage( - id="en-US", name="English", locale="en-US", threeLettersCode="en", twoLettersCode="en", progress=100 + id="en-US", + name="English", + locale="en-US", + dir="ltr", + threeLettersCode="en", + twoLettersCode="en", + progress=100, ), ) progress: list[dict] = self.get_progress()["data"] for model in models: - if model.locale in NAMES: - model.name = NAMES[model.locale] + if model.locale in LOCALE_DATA: + locale_data = LOCALE_DATA[model.locale] + model.name = locale_data.name + model.dir = locale_data.dir for p in progress: if p["data"]["languageId"] == model.id: diff --git a/docker/Dockerfile b/docker/Dockerfile index 919d27b7d007..1e786291bd79 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -138,4 +138,4 @@ EXPOSE ${APP_PORT} COPY ./docker/entry.sh $MEALIE_HOME/run.sh RUN chmod +x $MEALIE_HOME/run.sh -ENTRYPOINT $MEALIE_HOME/run.sh +ENTRYPOINT ["/app/run.sh"] diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml new file mode 100644 index 000000000000..71782872416f --- /dev/null +++ b/docker/docker-compose.dev.yml @@ -0,0 +1,21 @@ +version: "3.4" +services: + mailpit: + image: axllent/mailpit:latest + container_name: mealie_dev_mailpit + restart: no + environment: + - "MP_SMTP_AUTH_ACCEPT_ANY=true" + - "MP_SMTP_AUTH_ALLOW_INSECURE=true" + ports: + - "8025:8025" + - "1025:1025" + postgres: + container_name: mealie_dev_postgres + image: postgres:15 + restart: no + ports: + - "5432:5432" + environment: + POSTGRES_PASSWORD: mealie + POSTGRES_USER: mealie diff --git a/docker/entry.sh b/docker/entry.sh index 9bbddf1ecf3a..5bc077afc4da 100644 --- a/docker/entry.sh +++ b/docker/entry.sh @@ -1,13 +1,10 @@ -# Start Backend API #!/bin/bash - -# Strict Mode -# set -e -# IFS=$'\n\t' +# Start Backend API # Get PUID/PGID PUID=${PUID:-911} PGID=${PGID:-911} +BASH_SOURCE=${BASH_SOURCE:-$0} add_user() { groupmod -o -g "$PGID" abc @@ -15,18 +12,18 @@ add_user() { } change_user() { - # If container is started as root then create a new user and switch to it - if [ "$(id -u)" = "0" ]; then + if [ "$(id -u)" = $PUID ]; then + echo " + User uid: $PUID + User gid: $PGID + " + elif [ "$(id -u)" = "0" ]; then + # If container is started as root then create a new user and switch to it add_user chown -R $PUID:$PGID /app echo "Switching to dedicated user" exec gosu $PUID "$BASH_SOURCE" "$@" - elif [ "$(id -u)" = $PUID ]; then - echo " - User uid: $PUID - User gid: $PGID - " fi } @@ -41,7 +38,7 @@ init() { poetry run python /app/mealie/db/init_db.py } -# change_user +change_user init GUNICORN_PORT=${API_PORT:-9000} @@ -49,7 +46,7 @@ GUNICORN_PORT=${API_PORT:-9000} hostip=`/sbin/ip route|awk '/default/ { print $3 }'` if [ "$WEB_GUNICORN" = 'true' ]; then echo "Starting Gunicorn" - gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT --forwarded-allow-ips=$hostip -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload + exec gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT --forwarded-allow-ips=$hostip -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload else - uvicorn mealie.app:app --host 0.0.0.0 --forwarded-allow-ips=$hostip --port $GUNICORN_PORT + exec uvicorn mealie.app:app --host 0.0.0.0 --forwarded-allow-ips=$hostip --port $GUNICORN_PORT fi diff --git a/docs/docs/assets/img/ios/api.png b/docs/docs/assets/img/ios/api.png new file mode 100644 index 000000000000..179d4d21a332 Binary files /dev/null and b/docs/docs/assets/img/ios/api.png differ diff --git a/docs/docs/assets/img/ios/gemini.png b/docs/docs/assets/img/ios/gemini.png new file mode 100644 index 000000000000..3010a8cd2ea7 Binary files /dev/null and b/docs/docs/assets/img/ios/gemini.png differ diff --git a/docs/docs/assets/img/ios/setup.png b/docs/docs/assets/img/ios/setup.png new file mode 100644 index 000000000000..156e41a5b5b1 Binary files /dev/null and b/docs/docs/assets/img/ios/setup.png differ diff --git a/docs/docs/assets/img/ios/url.png b/docs/docs/assets/img/ios/url.png new file mode 100644 index 000000000000..7d8f1509ed36 Binary files /dev/null and b/docs/docs/assets/img/ios/url.png differ diff --git a/docs/docs/assets/img/sc1.jpg b/docs/docs/assets/img/sc1.jpg deleted file mode 100644 index 56e53fe1a591..000000000000 Binary files a/docs/docs/assets/img/sc1.jpg and /dev/null differ diff --git a/docs/docs/assets/img/sc1half.png b/docs/docs/assets/img/sc1half.png deleted file mode 100644 index c9236fd5dc51..000000000000 Binary files a/docs/docs/assets/img/sc1half.png and /dev/null differ diff --git a/docs/docs/assets/img/sc2.jpg b/docs/docs/assets/img/sc2.jpg deleted file mode 100644 index 3448e7d398a1..000000000000 Binary files a/docs/docs/assets/img/sc2.jpg and /dev/null differ diff --git a/docs/docs/assets/img/sc2half.png b/docs/docs/assets/img/sc2half.png deleted file mode 100644 index 83787aa494a7..000000000000 Binary files a/docs/docs/assets/img/sc2half.png and /dev/null differ diff --git a/docs/docs/assets/img/sc3.jpg b/docs/docs/assets/img/sc3.jpg deleted file mode 100644 index b8ac52eea2c6..000000000000 Binary files a/docs/docs/assets/img/sc3.jpg and /dev/null differ diff --git a/docs/docs/assets/img/sc3half.png b/docs/docs/assets/img/sc3half.png deleted file mode 100644 index efedca4f2aae..000000000000 Binary files a/docs/docs/assets/img/sc3half.png and /dev/null differ diff --git a/docs/docs/contributors/developers-guide/code-contributions.md b/docs/docs/contributors/developers-guide/code-contributions.md index 6ddbd20b1fe0..1bad2827b8a9 100644 --- a/docs/docs/contributors/developers-guide/code-contributions.md +++ b/docs/docs/contributors/developers-guide/code-contributions.md @@ -13,7 +13,7 @@ Pull requests are the best way to propose changes to the codebase (we use [Githu 3. If you're interested on working on major changes please get in touch on discord and coordinate with other developers. No sense in doubling up on work if someones already on it. 4. Once you've got an idea of what changes you want to make, create a draft PR as soon as you can to let us know what you're working on and how we can help! 5. If you've changed APIs, update the documentation. -6. Run tests, including `make backend-all`. Note that the tests do not clean up after themselves and leave things in the database. So be sure to also run `make clean-data` and/or `make backend-clean` inbetween major testing rounds to be sure that you aren't testing on old data. +6. Run tests, including `task py:check`. 6. Issue that pull request! First make a draft PR, make sure that the automated github tests all pass, then mark as ready for review. 7. Be sure to add release notes to the pull request. diff --git a/docs/docs/contributors/developers-guide/maintainers.md b/docs/docs/contributors/developers-guide/maintainers.md index f62c940848af..e2ef9e610661 100644 --- a/docs/docs/contributors/developers-guide/maintainers.md +++ b/docs/docs/contributors/developers-guide/maintainers.md @@ -1,6 +1,6 @@ # Maintainers Guide -This is the start of the maintainers guide for Mealie developers. Those who have been invited to the GitHub organization and/or those who whish to play a bigger part in the Mealie developers community may find this helpful. +This is the start of the maintainers guide for Mealie developers. Those who have been invited to the GitHub organization and/or those who wish to play a bigger part in the Mealie developers community may find this helpful. ## Managing Issues @@ -13,7 +13,7 @@ After you've reviered an issue it will generally move into one of two states: `needs more info` -: The orignal post does not contain enough information, and if the reporter does not provide additional information, the issue will be automatically closed. +: The original post does not contain enough information, and if the reporter does not provide additional information, the issue will be automatically closed. Once you've reviewed an issue and moved it into another category, you should remove the triage label. @@ -40,18 +40,38 @@ Mealie is published via GitHub actions to the GitHub container registry with the : published when a new GitHub Release is created - [Actions File](https://github.com/mealie-recipes/mealie/blob/mealie-next/.github/workflows/release.yml) !!! note - Both the latest, and {version} tags will be the same container on the release of a new version + Both the latest, and {version} tags will be the same image on the release of a new version ### Process -Because we've built all our publishing effors on GitHub Actions we rely primarily on automations to perform our releases. As such creating a new build of Mealie is as simple as creating a new GitHub release. Here are the general steps we take to create a new release +Because we've built all our publishing efforts on GitHub Actions we rely primarily on automations to perform our releases. As such creating a new build of Mealie is as simple as creating a new GitHub release. Here are the general steps we take to create a new release 1. Navigate to the [Github Release Page](https://github.com/mealie-recipes/mealie/releases) and click the 'Draft a new release' button. 2. Choose a tag and increment the version according to the semver specification. i.e, **major** version for breaking changes, **minor** for feature updates, and **patch** for bug fixes. -3. Name the Release, usually just the tag is fine, however if there is a special feature you'd like to higlight this would be a great place to do it. +3. Name the Release, usually just the tag is fine, however if there is a special feature you'd like to highlight this would be a great place to do it. 4. Click the "Generate release notes" button which will pull in all the Git Commits as a changelog. For bug fix only releases this is sufficient, however if there are major features, or good quality of life improvements it's good to provide those prior to listing the full changelog. !!! tip Don't worry about setting the version number in the container or code, it's set during the build process and uses the tag you specified when drafting a new release. You can see how this is done in the [Actions File](https://github.com/mealie-recipes/mealie/blob/mealie-next/.github/workflows/partial-builder.yml#L35-L37) + +### Tags and Releases + +Mealie tries to adhere to a strict [Semver](https://semver.org/) policy. This means that we try to keep our releases as stable as possible, and only introduce breaking changes when absolutely necessary. As such we try to keep our releases as follows: + +- **Major** releases are reserved for breaking changes, and are not expected to be frequent. Ideally, we will remain at v1.x.x for the forseeable future. +- **Minor** releases are reserved for new features, and are expected to be frequent. +- **Patch** releases are reserved for bug fixes, and are expected to be frequent. + +Any maintainer who has privileges on GitHub to create a new release can create a release at any time they feel it is necessary. However, it is recommended that you reach out in the discord to other maintainers and get at least one other maintainer to approve the release. + +An important caveat to this is that we _may_ make breaking changes in a minor release if it is security related. In this case, the releaser should headline the release notes with the notice and impact of the breaking change, however we may not bump the major version depending on user impact. + +### Release Notes + +When drafting a new release, GitHub will automatically pull in all the commits since the last release. This is a great start. After pulling in all of the commits, you should add sections for + +- New Features - Any new features that are being introduced in this release (screenshots are great here) +- Bug Fixes - Significant bug fixes that are being introduced in this release, smaller bug fixes can be left out if they are noted in a commit message +- Breaking Changes - Any breaking changes that are being introduced in this release (should be rare) diff --git a/docs/docs/contributors/developers-guide/starting-dev-server.md b/docs/docs/contributors/developers-guide/starting-dev-server.md index 55ab2f3451d2..d17772473ab7 100644 --- a/docs/docs/contributors/developers-guide/starting-dev-server.md +++ b/docs/docs/contributors/developers-guide/starting-dev-server.md @@ -14,12 +14,12 @@ Prerequisites - Visual Studio Code ### Linux and MacOS + First ensure that docker is running. Then when you clone the repo and open with VS Code you should see a popup asking you to reopen the project inside a development container. Click yes and it will build the development container and run the setup required to run both the backend API and the frontend webserver. This also pre-configures pre-commit hooks to ensure that the code is up to date before committing. ### Windows -Make sure the VSCode Dev Containers extension is installed, then select "Dev Containers: Clone Repository in Container Volume..." in the command pallete (F1). Select your forked repo and choose the `mealie-next` branch, which contains the latest changes. This mounts your repository directly in WSL2, which [greatly improves the performance of the container](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-a-git-repository-or-github-pr-in-an-isolated-container-volume), and enables hot-reloading for the frontend. Running the container on a mounted volume may not work correctly on Windows due to WSL permission mapping issues. -[Checkout the makefile reference](#make-file-reference) for all of the available commands. +Make sure the VSCode Dev Containers extension is installed, then select "Dev Containers: Clone Repository in Container Volume..." in the command palette (F1). Select your forked repo and choose the `mealie-next` branch, which contains the latest changes. This mounts your repository directly in WSL2, which [greatly improves the performance of the container](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-a-git-repository-or-github-pr-in-an-isolated-container-volume), and enables hot-reloading for the frontend. Running the container on a mounted volume may not work correctly on Windows due to WSL permission mapping issues. !!! tip For slow terminal checkout the solution in this [GitHub Issue](https://github.com/microsoft/vscode/issues/133215) @@ -29,16 +29,18 @@ Make sure the VSCode Dev Containers extension is installed, then select "Dev Con ``` ## Without Dev Containers + ### Prerequisites - [Python 3.10](https://www.python.org/downloads/) - [Poetry](https://python-poetry.org/docs/#installation) - [Node v16.x](https://nodejs.org/en/) - [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) +- [task](https://taskfile.dev/#/installation) ### Installing Dependencies -Once the prerequisites are installed you can cd into the project base directory and run `make setup` to install the python and node dependencies. +Once the prerequisites are installed you can cd into the project base directory and run `task setup` to install the python and node dependencies, and download the NLP model. === "Linux / macOS" @@ -46,29 +48,16 @@ Once the prerequisites are installed you can cd into the project base directory # Naviate To The Root Directory cd /path/to/project - # Utilize the Makefile to Install Dependencies - make setup + # Utilize the Taskfile to Install Dependencies + task setup ``` -=== "Windows" - - ``` powershell - # Install Python Dependencies - Set-Directory -Path "C:\path\to\project" - poetry install - - # Install Node Dependencies - Set-Directory frontend - yarn install - ``` - -### Setting ENV Variables - -Before you start the server you MUST copy the `template.env` and `frontend/template.env` files to their respective locations with the name `.env` and `frontend/.env` respectively. The application will-not run without these files. - ## Postgres -- Whether using a container or manual install, you need to set up your own postgres dev server. The database, username, password, etc should match the `POSTGRES_*` options located in the `.env` file. -- Install psycog2 with `poetry install -E pgsql` (in the main `mealie` directory, *not* `frontend`) + +The taskfile has two commands that need to be run to run the development environment against a postgres database. + +- `task dev:services` - This will start the postgres database, and a smtp server for email testing. +- `task py:postgres` - This will run that backend API configured for the local postgres database. ## Starting The Server @@ -78,57 +67,24 @@ Now you're ready to start the servers. You'll need two shells open, One for the ```bash # Terminal #1 - make backend + task py # Terminal #2 - make frontend + task ui ``` -=== "Windows" - - ``` powershell - # Terminal # 1 - poetry run python mealie/db/init_db.py # Initialize the database - poetry run python mealie/app.py # start application - - # Terminal # 2 - Set-Directory frontend - yarn run dev - ``` - -## Make File Reference - -Run `make help` for reference. If you're on a system that doesn't support makefiles in most cases you can use the commands directly in your terminal by copy/pasting them from the Makefile. - -``` -docs 📄 Start Mkdocs Development Server -code-gen 🤖 Run Code-Gen Scripts -setup 🏗 Setup Development Instance -setup-model 🤖 Get the latest NLP CRF++ Model -clean-data ⚠️ Removes All Developer Data for a fresh server start -clean-pyc 🧹 Remove Python file artifacts -clean-test 🧹 Remove test and coverage artifacts -backend-clean 🧹 Remove all build, test, coverage and Python artifacts -backend-test 🧪 Run tests quickly with the default Python -backend-format 🧺 Format, Check and Flake8 -backend-all 🧪 Runs all the backend checks and tests -backend-coverage ☂️ Check code coverage quickly with the default Python -backend 🎬 Start Mealie Backend Development Server -frontend 🎬 Start Mealie Frontend Development Server -frontend-build 🏗 Build Frontend in frontend/dist -frontend-generate 🏗 Generate Code for Frontend -frontend-lint 🧺 Run yarn lint -docker-dev 🐳 Build and Start Docker Development Stack (currently not functional, see #756, #1072) -docker-prod 🐳 Build and Start Docker Production Stack - -``` ## Internationalization + ### Frontend + We use vue-i18n package for internationalization. Translations are stored in json format located in [frontend/lang/messages](https://github.com/mealie-recipes/mealie/tree/mealie-next/frontend/lang/messages). + ### Backend + Translations are stored in json format located in [mealie/lang/messages](https://github.com/mealie-recipes/mealie/tree/mealie-next/mealie/lang/messages). ### Quick frontend localization with VS Code + [i18n Ally for VScode](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) is helpful for generating new strings to translate using Code Actions. It also has a nice feature, which shows translations in-place when editing code. A few settings must be tweaked to make the most of its features. Some settings are stored on project level, but most of them have to be set manually in your workspace or user settings.\ diff --git a/docs/docs/documentation/community-guide/ios.md b/docs/docs/documentation/community-guide/ios.md index dfdb0127e1ec..11a875e93d36 100644 --- a/docs/docs/documentation/community-guide/ios.md +++ b/docs/docs/documentation/community-guide/ios.md @@ -3,37 +3,80 @@ !!! info This guide was submitted by a community member. Find something wrong? Submit a PR to get it fixed! -![Image from apple site](https://help.apple.com/assets/5E8CEA35094622DF10489984/5E8CEA42094622DF1048998D/en_US/ed1f9c157cdefc13e0161e0f70015455.png) - -User [brasilikum](https://github.com/brasilikum) opened an issue on the main repo about how they had created an [iOS shortcut](https://github.com/mealie-recipes/mealie/issues/103) for interested users. -This original method broke after the transition to version 1.X and an issue was raised on [Github](https://github.com/mealie-recipes/mealie/issues/2092) GitHub user [Zippyy](https://github.com/zippyy) has helped to create a working shortcut for version 1.X. - -This is a useful utility for iOS users who browse for recipes in their web browser from their devices. - Don't know what an iOS shortcut is? Neither did I! Experienced iOS users may already be familiar with this utility but for the uninitiated, here is the official Apple explanation: > A shortcut is a quick way to get one or more tasks done with your apps. The Shortcuts app lets you create your own shortcuts with multiple steps. For example, build a “Surf Time” shortcut that grabs the surf report, gives an ETA to the beach, and launches your surf music playlist. -Basically it is a visual scripting language that lets a user build an automation in a guided fashion. The automation can be [shared with anyone](https://www.icloud.com/shortcuts/cc568d1615bc4f998789f85d1ef74846) but if it is a user creation, you'll have to jump through a few hoops to make an untrusted automation work on your device. +Basically it is a visual scripting language that lets a user build an automation in a guided fashion. The automation can be [shared with anyone](https://www.icloud.com/shortcuts/94aa272af5ff4d2c8fe5e13a946f89a9) but if it is a user creation, you'll have to jump through a few hoops to make an untrusted automation work on your device. -This guide assumes that you already know how to [generate API tokens](https://hay-kot.github.io/mealie/documentation/users-groups/user-settings/#api-key-generation) for your user that intends to use an iOS shortcut. +## Setup Video -First, click the [link](https://www.icloud.com/shortcuts/cc568d1615bc4f998789f85d1ef74846) and begin the setup of the shortcut. +The following YouTube video walks through setting up the shortcut in 3 minutes for those who prefer following along visually. + -![screenshot](../../assets/img/sc1half.png) +## Guide + +### Prerequisites + +Before setting up the shortcut, make sure you have the following information ready and easily accessable on your Apple device. + +1. The URL of your Mealie instance +2. An API Key for your user +3. A Gemini API Key from [Google AI Studio](https://makersuite.google.com) + +!!! note + A Gemini API Key is not required for importing URLs from Safari or your Camera, however you will not be able to take a photo of a recipe and import it without a Gemini key. + + Google AI Studio is currently only available in [certain countries and languages](https://ai.google.dev/available_regions). Most notably it is not currently available in Europe. + +### Setup + +On the Apple device you wish to add the shortcut to, click on [this link](https://www.icloud.com/shortcuts/94aa272af5ff4d2c8fe5e13a946f89a9) to begin the setup of the shortcut. + +![screenshot](../../assets/img/ios/setup.png) Next, you need to replace `url` and `port` with the information for your Mealie instance. If you have a domain that you use (e.g. `https://mealie.example.com`), put that here. If you just run local, then you need to put in your Mealie instance IP and the port you use (e.g. the default is `9925`). +![screenshot](../../assets/img/ios/url.png) -![screenshot](../../assets/img/sc2half.png) +Next, you need to replace `MEALIE_API_KEY` with your API token. +![screenshot](../../assets/img/ios/api.png) -Finally, you need to replace the word `keyhere` with your API token. Keep the word `Bearer`!!! +Finally, replace `GEMINI_API_KEY` with the one you got from [Google AI Studio](https://makersuite.google.com) +![screenshot](../../assets/img/ios/gemini.png) -![screenshot](../../assets/img/sc3half.png) +You may wish to [add the shortcut to your home screen](https://support.apple.com/guide/shortcuts/add-a-shortcut-to-the-home-screen-apd735880972/ios) for easier access. -You should now be able to share a website to the shortcut and have Mealie grab all the necessary information! +## Features + +- Share a website from Safari with Mealie to import via URL. +- Share a recipe photo from photos to perform OCR and import a physical recipe. +- Trigger the shortcut and take a photo of a physical recipe to import. +- Trigger the shortcut to select a photo from your Photos app to import. +- Trigger the shortcut to take a picture of a URL (like on the bottom of a printed recipe) to import. + +## Troubleshooting + +Sometimes Gemini will not be able to parse a recipe, and you will get an error. Users have found success with a combination of the following: + +1. #### Try Again + Sometimes Gemini returns the wrong information which causes the import to fail. Often, trying again will be successful. + +2. #### Photo Quality + Make sure there is no large glare or shadow over the picture, and you have all the text in frame. + +3. #### Edit the Photo + Users have found success by cropping the picture to just the recipe card, adding a "mono" filter, and cranking up the exposure before importing. + +## History + +User [brasilikum](https://github.com/brasilikum) opened an issue on the main repo about how they had created an [iOS shortcut](https://github.com/mealie-recipes/mealie/issues/103) for interested users. + +This original method broke after the transition to version 1.X and an issue was raised on [Github](https://github.com/mealie-recipes/mealie/issues/2092) GitHub user [Zippyy](https://github.com/zippyy) has helped to create a working shortcut for version 1.X. + +When OCR was removed from Mealie, GitHub user [hunterjm](https://github.com/zippyy) created a new shortcut that uses Apple's built-in OCR and Google Gemini to enhance and replace that functionality. diff --git a/docs/docs/documentation/community-guide/swag.md b/docs/docs/documentation/community-guide/swag.md index 3751271e6648..682b205aa90d 100644 --- a/docs/docs/documentation/community-guide/swag.md +++ b/docs/docs/documentation/community-guide/swag.md @@ -5,7 +5,7 @@ -To make the setup of a Reverse Proxy much easier, Linuxserver.io developed [SWAG](https://github.com/linuxserver/docker-swag) +To make the setup of a Reverse Proxy much easier, Linuxserver.io developed [SWAG](https://github.com/linuxserver/docker-swag) SWAG - Secure Web Application Gateway (formerly known as letsencrypt, no relation to Let's Encrypt™) sets up an Nginx web server and reverse proxy with PHP support and a built-in certbot client that automates free SSL server certificate generation and renewal processes (Let's Encrypt and ZeroSSL). It also contains fail2ban for intrusion prevention. ## Step 1: Get a domain @@ -14,39 +14,38 @@ The first step is to grab a dynamic DNS if you don't have your own subdomain alr ## Step 2: Set-up SWAG -Then you will need to set up SWAG, the variables of the docker-compose are explained on the Github page of [SWAG](https://github.com/linuxserver/docker-swag). -This is an example of how to set it up using duckdns and docker-compose. +Then you will need to set up SWAG, the variables of the docker-compose.yaml file are explained on the Github page of [SWAG](https://github.com/linuxserver/docker-swag). +This is an example of how to set it up using duckdns and docker compose. -!!! example "docker-compose.yml" +!!! example "docker-compose.yaml" ```yaml version: "3.1" services: -swag: -image: ghcr.io/linuxserver/swag -container_name: swag -cap_add: -- NET_ADMIN -environment: -- PUID=1000 -- PGID=1000 -- TZ=Europe/Brussels -- URL= -- SUBDOMAINS=wildcard -- VALIDATION=duckdns -- CERTPROVIDER= #optional -- DNSPLUGIN= #optional -- DUCKDNSTOKEN= -- EMAIL= #optional -- ONLY_SUBDOMAINS=false #optional -- EXTRA_DOMAINS= #optional -- STAGING=false #optional -volumes: -- /etc/config/swag:/config -ports: -- 443:443 -- 80:80 #optional -restart: unless-stopped - + swag: + image: ghcr.io/linuxserver/swag + container_name: swag + cap_add: + - NET_ADMIN + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Brussels + - URL= + - SUBDOMAINS=wildcard + - VALIDATION=duckdns + - CERTPROVIDER= #optional + - DNSPLUGIN= #optional + - DUCKDNSTOKEN= + - EMAIL= #optional + - ONLY_SUBDOMAINS=false #optional + - EXTRA_DOMAINS= #optional + - STAGING=false #optional + volumes: + - /etc/config/swag:/config + ports: + - 443:443 + - 80:80 #optional + restart: unless-stopped ``` Don't forget to change the mydomain.duckns into your personal domain and the duckdnstoken into your token and remove the brackets. @@ -61,26 +60,25 @@ Alternatively, you can create a new file mealie.subdomain.conf in p !!! example "mealie.subdomain.conf" ```yaml - server { - listen 443 ssl http2; - listen [::]:443 ssl http2; +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; - server_name mealie.*; + server_name mealie.*; - include /config/nginx/ssl.conf; + include /config/nginx/ssl.conf; - client_max_body_size 0; + client_max_body_size 0; - location / { - include /config/nginx/proxy.conf; - include /config/nginx/resolver.conf; - set $upstream_app mealie-frontend; - set $upstream_port 3000; - set $upstream_proto http; - proxy_pass $upstream_proto://$upstream_app:$upstream_port; - } - - } + location / { + include /config/nginx/proxy.conf; + include /config/nginx/resolver.conf; + set $upstream_app mealie-frontend; + set $upstream_port 3000; + set $upstream_proto http; + proxy_pass $upstream_proto://$upstream_app:$upstream_port; + } +} ``` ## Step 4: Port-forward port 443 diff --git a/docs/docs/documentation/getting-started/faq.md b/docs/docs/documentation/getting-started/faq.md index a7a839bc6ef2..76bb5693837a 100644 --- a/docs/docs/documentation/getting-started/faq.md +++ b/docs/docs/documentation/getting-started/faq.md @@ -41,22 +41,15 @@ Yes. If you are using the v1 branches (including beta), you can upgrade to the l ## How can I change the theme? -You can change the theme by settings the environment variables on the frontend container. +You can change the theme by settings the environment variables. -- [Frontend Theme](../installation/frontend-config#themeing) - -## How can I change the language? - -Languages need to be set on the frontend and backend containers as ENV variables. - -- [Frontend Config](../installation/frontend-config/) -- [Backend Config](../installation/backend-config/) +- [Backend Config - Themeing](./installation/backend-config.md#themeing) ## How can I change the Login Session Timeout? Login session can be configured by setting the `TOKEN_TIME` variable on the backend container. -- [Backend Config](../installation/backend-config/) +- [Backend Config](./installation/backend-config.md) ## Can I serve Mealie on a subpath? diff --git a/docs/docs/documentation/getting-started/installation/installation-checklist.md b/docs/docs/documentation/getting-started/installation/installation-checklist.md index 2391d2d3a01d..5c16abefdd0e 100644 --- a/docs/docs/documentation/getting-started/installation/installation-checklist.md +++ b/docs/docs/documentation/getting-started/installation/installation-checklist.md @@ -72,7 +72,7 @@ After you've decided setup the files it's important to set a few ENV variables t 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`. ```bash -$ docker-compose up -d +$ docker compose up -d ``` You should see the containers start up without error. You should now be able to access the Mealie frontend at [http://localhost:9925](http://localhost:9925). @@ -107,13 +107,11 @@ See all available tags on [GitHub](https://github.com/mealie-recipes/mealie/pkgs The nightly build are the latest and greatest builds that are built directly off of every commit to the `mealie-next` branch and as such may contain bugs. These are great to help the community catch bugs before they hit the stable release or if you like living on the edge. -`ghrc.io/mealie-recipes/mealie:` +`ghcr.io/mealie-recipes/mealie:` We also provide versioned containers that allow to pin to a specific release. Each time a new release is built a new tag will be pushed with the version. These are great to pin to a specific version and allows you to have absolute control on when you upgrade your container. -`ghrc.io/mealie-recipes/mealie:latest` - -_Note: This tag is not yet available; it will be available with the v1 stable release_ +`ghcr.io/mealie-recipes/mealie:latest` The latest tag provides the latest released image of Mealie. diff --git a/docs/docs/documentation/getting-started/installation/postgres.md b/docs/docs/documentation/getting-started/installation/postgres.md index f98f6b22a7ff..79f428a2b6ab 100644 --- a/docs/docs/documentation/getting-started/installation/postgres.md +++ b/docs/docs/documentation/getting-started/installation/postgres.md @@ -2,23 +2,21 @@ PostgreSQL might be considered if you need to support many concurrent users. In addition, some features are only enabled on PostgreSQL, such as fuzzy search. -**For Environmental Variable Configuration See:** - -- [Configuration](./backend-config.md) +**For Environment Variable Configuration, see** [Backend Configuration](./backend-config.md) ```yaml --- version: "3.7" services: mealie: - image: ghcr.io/mealie-recipes/mealie:v1.0.0-RC1.1 + image: ghcr.io/mealie-recipes/mealie:v1.1.0 # (3) container_name: mealie ports: - - "9925:9000" + - "9925:9000" # (1) deploy: resources: limits: - memory: 1000M # (1) + memory: 1000M # (2) depends_on: - postgres volumes: @@ -62,3 +60,4 @@ volumes: 1. To access the mealie interface you only need to expose port 9000 on the mealie container. Here we expose port 9925 on the host, but feel free to change this to any port you like. 2. Setting an explicit memory limit is recommended. Python can pre-allocate larger amounts of memory than is necessary if you have a machine with a lot of RAM. This can cause the container to idle at a high memory usage. Setting a memory limit will improve idle performance. +3. You should double check this value isn't out of date when setting up for the first time; check the README and use the value from the "latest release" badge at the top - the format should be `vX.Y.Z`. Whilst a 'latest' tag is available, the Mealie team advises specifying a specific version tag and consciously updating to newer versions when you have time to read the release notes and ensure you follow any manual actions required (which should be rare). diff --git a/docs/docs/documentation/getting-started/installation/sqlite.md b/docs/docs/documentation/getting-started/installation/sqlite.md index 5b63f9634149..5de3f7e19152 100644 --- a/docs/docs/documentation/getting-started/installation/sqlite.md +++ b/docs/docs/documentation/getting-started/installation/sqlite.md @@ -1,17 +1,19 @@ # Installing with SQLite +!!! Warning + If you're planning on deploying and using Network Attached Storage with Mealie, you should use [Postgres](./postgres.md) instead of SQLite. SQLite is not designed to be used with Network Attached Storage and can cause data corruption, or locked database errors + + SQLite is a popular, open source, self-contained, zero-configuration database that is the ideal choice for Mealie when you have 1-20 Users. Below is a ready to use docker-compose.yaml file for deploying Mealie on your server. -**For Environmental Variable Configuration See:** - -- [Configuration](./backend-config.md) +**For Environment Variable Configuration, see** [Backend Configuration](./backend-config.md) ```yaml --- version: "3.7" services: mealie: - image: ghcr.io/mealie-recipes/mealie:v1.0.0-RC1.1 + image: ghcr.io/mealie-recipes/mealie:v1.1.0 # (3) container_name: mealie ports: - "9925:9000" # (1) @@ -41,3 +43,4 @@ volumes: 1. To access the mealie interface you only need to expose port 9000 on the container. Here we expose port 9925 on the host, but feel free to change this to any port you like. 2. Setting an explicit memory limit is recommended. Python can pre-allocate larger amounts of memory than is necessary if you have a machine with a lot of RAM. This can cause the container to idle at a high memory usage. Setting a memory limit will improve idle performance. +3. You should double check this value isn't out of date when setting up for the first time; check the README and use the value from the "latest release" badge at the top - the format should be `vX.Y.Z`. Whilst a 'latest' tag is available, the Mealie team advises specifying a specific version tag and consciously updating to newer versions when you have time to read the release notes and ensure you follow any manual actions required (which should be rare). diff --git a/docs/docs/documentation/getting-started/introduction.md b/docs/docs/documentation/getting-started/introduction.md index 776d9312043f..010de5edf569 100644 --- a/docs/docs/documentation/getting-started/introduction.md +++ b/docs/docs/documentation/getting-started/introduction.md @@ -1,11 +1,5 @@ # About The Project -!!! warning "Mealie v1 Beta Release" - - This documentation is for the Mealie v1 Beta release and is not final. As such, it may contain incomplete or incorrect information. You should understand that installing Mealie v1 Beta is a work in progress and while we've committed to maintaining the database schema and provided migrations, we are still in the process of adding new features, and robust testing to ensure the application works as expected. - - You should likely find bugs, errors, and unfinished pages within the application. To find the current status of the release you can checkout the [project on github](https://github.com/mealie-recipes/mealie/projects/7) or reach out on discord. - Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and Mealie will automatically import the relevant data or add a family recipe with the UI editor. Mealie also provides an API for interactions from 3rd party applications. [Remember to join the Discord](https://discord.gg/QuStdQGSGK) diff --git a/docs/docs/documentation/getting-started/updating.md b/docs/docs/documentation/getting-started/updating.md index 72eca9fb717d..6917bc35bb9c 100644 --- a/docs/docs/documentation/getting-started/updating.md +++ b/docs/docs/documentation/getting-started/updating.md @@ -19,6 +19,7 @@ If you are upgrading from pre-v1.0.0 to v1.0.0, make sure you read [Migrating to ## Docker For all setups using Docker the updating process looks something like this -- Stop the container using `docker-compose down` -- Pull the latest image using `docker-compose pull` -- Start the container again using `docker-compose up -d` +- Stop the container using `docker compose down` +- If you are not using the latest tag, change the version (image tag) in your docker-compose file +- Pull the latest image using `docker compose pull` +- Start the container again using `docker compose up -d` diff --git a/docs/docs/overrides/api.html b/docs/docs/overrides/api.html index 83038e2aead5..28fdc391d2c4 100644 --- a/docs/docs/overrides/api.html +++ b/docs/docs/overrides/api.html @@ -14,7 +14,7 @@
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 28194174b136..fc5badfd6120 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -51,8 +51,8 @@ extra_css: - assets/stylesheets/custom.css extra_javascript: - assets/js/extra.js -repo_url: https://github.com/hay-kot/mealie/ -repo_name: hay-kot/mealie +repo_url: https://github.com/mealie-recipes/mealie/ +repo_name: mealie-recipes/mealie edit_uri: edit/mealie-next/docs/docs/ nav: diff --git a/frontend/components/Domain/Cookbook/CookbookEditor.vue b/frontend/components/Domain/Cookbook/CookbookEditor.vue new file mode 100644 index 000000000000..6f733f485b97 --- /dev/null +++ b/frontend/components/Domain/Cookbook/CookbookEditor.vue @@ -0,0 +1,55 @@ + + + diff --git a/frontend/components/Domain/Recipe/RecipeActionMenu.vue b/frontend/components/Domain/Recipe/RecipeActionMenu.vue index ad24e0ed8b13..4fd418ffbb34 100644 --- a/frontend/components/Domain/Recipe/RecipeActionMenu.vue +++ b/frontend/components/Domain/Recipe/RecipeActionMenu.vue @@ -139,7 +139,7 @@ export default defineComponent({ default: false, }, }, - setup(props, context) { + setup(_, context) { const deleteDialog = ref(false); const { i18n, $globals } = useContext(); diff --git a/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue b/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue index a3621043685c..1992ca3797ee 100644 --- a/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue +++ b/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue @@ -26,54 +26,69 @@ >
- + - {{ section.recipeName }} + {{ recipeSection.recipeName }} - + - ({{ $tc("recipe.quantity") }}: {{ section.recipeScale }}) + ({{ $tc("recipe.quantity") }}: {{ recipeSection.recipeScale }}) -
- +
- - - - - + + {{ ingredientSection.sectionName }} + +
+ + + + + + +
+
@@ -112,17 +127,22 @@ export interface RecipeWithScale extends Recipe { scale: number; } -export interface ShoppingListRecipeIngredient { +export interface ShoppingListIngredient { checked: boolean; ingredient: RecipeIngredient; disableAmount: boolean; } +export interface ShoppingListIngredientSection { + sectionName: string; + ingredients: ShoppingListIngredient[]; +} + export interface ShoppingListRecipeIngredientSection { recipeId: string; recipeName: string; recipeScale: number; - ingredients: ShoppingListRecipeIngredient[]; + ingredientSections: ShoppingListIngredientSection[]; } export default defineComponent({ @@ -191,7 +211,7 @@ export default defineComponent({ continue; } - const shoppingListIngredients: ShoppingListRecipeIngredient[] = recipe.recipeIngredient.map((ing) => { + const shoppingListIngredients: ShoppingListIngredient[] = recipe.recipeIngredient.map((ing) => { return { checked: true, ingredient: ing, @@ -199,11 +219,35 @@ export default defineComponent({ } }); + const shoppingListIngredientSections = shoppingListIngredients.reduce((sections, ing) => { + // if title append new section to the end of the array + if (ing.ingredient.title) { + sections.push({ + sectionName: ing.ingredient.title, + ingredients: [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 + sections[sections.length - 1].ingredients.push(ing); + return sections; + }, [] as ShoppingListIngredientSection[]); + recipeSectionMap.set(recipe.slug, { recipeId: recipe.id, recipeName: recipe.name, recipeScale: recipe.scale, - ingredients: shoppingListIngredients, + ingredientSections: shoppingListIngredientSections, }) } @@ -231,9 +275,11 @@ export default defineComponent({ } function bulkCheckIngredients(value = true) { - recipeIngredientSections.value.forEach((section) => { - section.ingredients.forEach((ing) => { - ing.checked = value; + recipeIngredientSections.value.forEach((recipeSection) => { + recipeSection.ingredientSections.forEach((ingSection) => { + ingSection.ingredients.forEach((ing) => { + ing.checked = value; + }); }); }); } @@ -246,10 +292,12 @@ export default defineComponent({ } const ingredients: RecipeIngredient[] = []; - section.ingredients.forEach((ing) => { - if (ing.checked) { - ingredients.push(ing.ingredient); - } + section.ingredientSections.forEach((ingSection) => { + ingSection.ingredients.forEach((ing) => { + if (ing.checked) { + ingredients.push(ing.ingredient); + } + }); }); if (!ingredients.length) { @@ -272,7 +320,11 @@ export default defineComponent({ } }) - success ? alert.success(i18n.t("recipe.recipes-added-to-list") as string) + const successMessage = promises.length === 1 + ? i18n.t("recipe.successfully-added-to-list") as string + : i18n.t("recipe.failed-to-add-to-list") as string; + + success ? alert.success(successMessage) : alert.error(i18n.t("failed-to-add-recipes-to-list") as string) state.shoppingListDialog = false; diff --git a/frontend/components/Domain/Recipe/RecipeIngredients.vue b/frontend/components/Domain/Recipe/RecipeIngredients.vue index 8be0b433a3db..3769243c13f3 100644 --- a/frontend/components/Domain/Recipe/RecipeIngredients.vue +++ b/frontend/components/Domain/Recipe/RecipeIngredients.vue @@ -52,11 +52,20 @@ export default defineComponent({ }); const ingredientCopyText = computed(() => { - return props.value - .map((ingredient) => { - return `${parseIngredientText(ingredient, props.disableAmount, props.scale, false)}`; - }) - .join("\n"); + const components: string[] = []; + props.value.forEach((ingredient) => { + if (ingredient.title) { + if (components.length) { + components.push(""); + } + + components.push(`[${ingredient.title}]`); + } + + components.push(parseIngredientText(ingredient, props.disableAmount, props.scale, false)); + }); + + return components.join("\n"); }); function toggleChecked(index: number) { diff --git a/frontend/components/Domain/Recipe/RecipeNotes.vue b/frontend/components/Domain/Recipe/RecipeNotes.vue index f5edcaaf672a..2139ef0a9ad4 100644 --- a/frontend/components/Domain/Recipe/RecipeNotes.vue +++ b/frontend/components/Domain/Recipe/RecipeNotes.vue @@ -1,7 +1,7 @@