Skip to content

Commit

Permalink
Merge branch 'develop' into beta
Browse files Browse the repository at this point in the history
  • Loading branch information
vabene1111 committed Jan 1, 2025
2 parents 88347d4 + 6444680 commit 954a35b
Show file tree
Hide file tree
Showing 20 changed files with 188 additions and 70 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,5 @@ vue3/.vite
# Configs
vetur.config.js
venv/
.idea/easy-i18n.xml
cookbook/static/vue3
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-de
#Copy project and execute it.
COPY . ./

HEALTHCHECK --interval=30s \
--timeout=5s \
--start-period=10s \
--retries=3 \
CMD [ "/usr/bin/wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8080/openapi" ]

# collect information from git repositories
RUN /opt/recipes/venv/bin/python version.py
# delete git repositories to reduce image size
Expand Down
14 changes: 13 additions & 1 deletion boot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ if [ -z "${SECRET_KEY}" ]; then
display_warning "The environment variable 'SECRET_KEY' (or 'SECRET_KEY_FILE' that points to an existing file) is not set but REQUIRED for running Tandoor!"
fi

if [ -f "${AUTH_LDAP_BIND_PASSWORD_FILE}" ]; then
export AUTH_LDAP_BIND_PASSWORD=$(cat "$AUTH_LDAP_BIND_PASSWORD_FILE")
fi

if [ -f "${EMAIL_HOST_PASSWORD_FILE}" ]; then
export EMAIL_HOST_PASSWORD=$(cat "$EMAIL_HOST_PASSWORD_FILE")
fi

if [ -f "${SOCIALACCOUNT_PROVIDERS_FILE}" ]; then
export SOCIALACCOUNT_PROVIDERS=$(cat "$SOCIALACCOUNT_PROVIDERS_FILE")
fi


echo "Waiting for database to be ready..."

Expand Down Expand Up @@ -83,4 +95,4 @@ if [ "$ipv6_disable" -eq 0 ]; then
exec gunicorn -b "[::]:$TANDOOR_PORT" --workers $GUNICORN_WORKERS --threads $GUNICORN_THREADS --access-logfile - --error-logfile - --log-level $GUNICORN_LOG_LEVEL recipes.wsgi
else
exec gunicorn -b ":$TANDOOR_PORT" --workers $GUNICORN_WORKERS --threads $GUNICORN_THREADS --access-logfile - --error-logfile - --log-level $GUNICORN_LOG_LEVEL recipes.wsgi
fi
fi
38 changes: 25 additions & 13 deletions cookbook/forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from datetime import datetime


from allauth.account.forms import ResetPasswordForm, SignupForm
from allauth.socialaccount.forms import SignupForm as SocialSignupForm
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
Expand All @@ -14,15 +16,13 @@


class SelectWidget(widgets.Select):

class Media:
js = ('custom/js/form_select.js', )
js = ('custom/js/form_select.js',)


class MultiSelectWidget(widgets.SelectMultiple):

class Media:
js = ('custom/js/form_multiselect.js', )
js = ('custom/js/form_multiselect.js',)


# Yes there are some stupid browsers that still dont support this but
Expand Down Expand Up @@ -139,7 +139,7 @@ class CommentForm(forms.ModelForm):

class Meta:
model = Comment
fields = ('text', )
fields = ('text',)

labels = {'text': _('Add your comment: '), }
widgets = {'text': forms.Textarea(attrs={'rows': 2, 'cols': 15}), }
Expand All @@ -161,7 +161,6 @@ class Meta:
help_texts = {'url': _('Leave empty for dropbox and enter only base url for nextcloud (<code>/remote.php/webdav/</code> is added automatically)'), }



class ConnectorConfigForm(forms.ModelForm):
enabled = forms.BooleanField(
help_text="Is the connector enabled",
Expand Down Expand Up @@ -315,6 +314,18 @@ def signup(self, request, user):
pass


class AllAuthSocialSignupForm(SocialSignupForm):
terms = forms.BooleanField(label=_('Accept Terms and Privacy'))

def __init__(self, **kwargs):
super().__init__(**kwargs)
if settings.PRIVACY_URL == '' and settings.TERMS_URL == '':
self.fields.pop('terms')

def signup(self, request, user):
pass


class CustomPasswordResetForm(ResetPasswordForm):
captcha = hCaptchaField()

Expand Down Expand Up @@ -345,12 +356,13 @@ class Meta:

help_texts = {
'search': _('Select type method of search. Click <a href="/docs/search/">here</a> for full description of choices.'), 'lookup':
_('Use fuzzy matching on units, keywords and ingredients when editing and importing recipes.'), 'unaccent':
_('Fields to search ignoring accents. Selecting this option can improve or degrade search quality depending on language'), 'icontains':
_("Fields to search for partial matches. (e.g. searching for 'Pie' will return 'pie' and 'piece' and 'soapie')"), 'istartswith':
_("Fields to search for beginning of word matches. (e.g. searching for 'sa' will return 'salad' and 'sandwich')"), 'trigram':
_("Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) Note: this option will conflict with 'web' and 'raw' methods of search."), 'fulltext':
_("Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields."),
_('Use fuzzy matching on units, keywords and ingredients when editing and importing recipes.'), 'unaccent':
_('Fields to search ignoring accents. Selecting this option can improve or degrade search quality depending on language'), 'icontains':
_("Fields to search for partial matches. (e.g. searching for 'Pie' will return 'pie' and 'piece' and 'soapie')"), 'istartswith':
_("Fields to search for beginning of word matches. (e.g. searching for 'sa' will return 'salad' and 'sandwich')"), 'trigram':
_("Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) Note: this option will conflict with 'web' and 'raw' methods of search."),
'fulltext':
_("Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields."),
}

labels = {
Expand All @@ -360,5 +372,5 @@ class Meta:

widgets = {
'search': SelectWidget, 'unaccent': MultiSelectWidget, 'icontains': MultiSelectWidget, 'istartswith': MultiSelectWidget, 'trigram': MultiSelectWidget, 'fulltext':
MultiSelectWidget,
MultiSelectWidget,
}
5 changes: 2 additions & 3 deletions cookbook/helper/recipe_url_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.utils.translation import gettext as _
from isodate import parse_duration as iso_parse_duration
from isodate.isoerror import ISO8601Error
from pytube import YouTube
from pytubefix import YouTube
from recipe_scrapers._utils import get_host_name, get_minutes

from cookbook.helper.automation_helper import AutomationEngine
Expand Down Expand Up @@ -274,9 +274,8 @@ def get_from_youtube_scraper(url, request):
default_recipe_json['image'] = video.thumbnail_url
if video.description:
default_recipe_json['steps'][0]['instruction'] = automation_engine.apply_regex_replace_automation(video.description, Automation.INSTRUCTION_REPLACE)

except Exception:
pass
traceback.print_exc()

return default_recipe_json

Expand Down
8 changes: 6 additions & 2 deletions cookbook/helper/template_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import bleach
import markdown as md
from jinja2 import Template, TemplateSyntaxError, UndefinedError
from jinja2.exceptions import SecurityError
from jinja2.sandbox import SandboxedEnvironment
from markdown.extensions.tables import TableExtension

from cookbook.helper.mdx_attributes import MarkdownFormatExtension
Expand Down Expand Up @@ -89,11 +91,13 @@ def scale(number):
return f"<scalable-number v-bind:number='{bleach.clean(str(number))}' v-bind:factor='ingredient_factor'></scalable-number>"

try:
template = Template(instructions)
instructions = template.render(ingredients=ingredients, scale=scale)
env = SandboxedEnvironment()
instructions = env.from_string(instructions).render(ingredients=ingredients, scale=scale)
except TemplateSyntaxError:
return _('Could not parse template code.') + ' Error: Template Syntax broken'
except UndefinedError:
return _('Could not parse template code.') + ' Error: Undefined Error'
except SecurityError:
return _('Could not parse template code.') + ' Error: Security Error'

return instructions
17 changes: 9 additions & 8 deletions cookbook/locale/it/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
"PO-Revision-Date: 2024-11-01 06:58+0000\n"
"PO-Revision-Date: 2024-12-09 00:58+0000\n"
"Last-Translator: Vincenzo Reale <[email protected]>\n"
"Language-Team: Italian <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/it/>\n"
Expand All @@ -21,7 +21,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.6.2\n"
"X-Generator: Weblate 5.8.4\n"

#: .\cookbook\forms.py:45
msgid ""
Expand Down Expand Up @@ -520,7 +520,7 @@ msgstr "Web"

#: .\cookbook\models.py:1411 .\cookbook\templates\search_info.html:47
msgid "Raw"
msgstr "Raw"
msgstr "Crudo"

#: .\cookbook\models.py:1467
msgid "Food Alias"
Expand Down Expand Up @@ -1440,8 +1440,9 @@ msgid ""
"\"noreferrer noopener\" target=\"_blank\">this one.</a>"
msgstr ""
"Le tabelle in markdown sono difficili da creare a mano. Si raccomanda "
"l'utilizzo di un editor di come <a href=\"https://www.tablesgenerator.com/"
"markdown_tables\" rel=\"noreferrer noopener\" target=\"_blank\">questo.</a>"
"l'utilizzo di un editor di tabelle come <a href=\"https://www.tablesgenerator"
".com/markdown_tables\" rel=\"noreferrer noopener\" target=\"_blank\""
">questo.</a>"

#: .\cookbook\templates\markdown_info.html:155
#: .\cookbook\templates\markdown_info.html:157
Expand Down Expand Up @@ -2203,8 +2204,8 @@ msgstr ""
" Le migrazioni non andate a buon fine probabilmente causeranno il "
"malfunzionamento di parti importanti dell'applicazione.\n"
" Se una migrazione non riesce, assicurati di avere la versione "
"più recente e, in tal caso, pubblica il registro della migrazione e la "
"panoramica di seguito in una segnalazione di problema su GitHub.\n"
"più recente e, in tal caso, pubblica il registro della migrazione e il "
"riepilogo che segue in una segnalazione di problema su GitHub.\n"
" "

#: .\cookbook\templates\system.html:182
Expand Down Expand Up @@ -2765,7 +2766,7 @@ msgid ""
"but not recommended as some features only work with postgres databases."
msgstr ""
"Questa applicazione non è in esecuzione con un database Postgres. Va bene, "
"ma non è consigliato perché alcune funzionalità sono disponibili solo con un "
"ma non è consigliato perché alcune funzionalità sono disponibili solo con "
"database Postgres."

#: .\cookbook\views\views.py:360
Expand Down
1 change: 0 additions & 1 deletion cookbook/templates/account/signup.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{% extends "base.html" %}
{% load crispy_forms_filters %}
{% load crispy_forms_filters %}
{% load i18n %}

{% block title %}{% trans 'Register' %}{% endblock %}
Expand Down
3 changes: 1 addition & 2 deletions cookbook/templates/socialaccount/signup.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ <h1>{% trans "Sign Up" %}</h1>
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
{% endif %}

<div class="form-group">
{{ form.username |as_crispy_field }}
</div>
Expand All @@ -30,7 +29,7 @@ <h1>{% trans "Sign Up" %}</h1>
<div class="form-group">
{{ form.terms |as_crispy_field }}
<small>
{% trans 'I accept the follwoing' %}
{% trans 'I accept the following' %}
{% if TERMS_URL != '' %}
<a href="{{ TERMS_URL }}" target="_blank"
rel="noreferrer nofollow">{% trans 'Terms and Conditions' %}</a>
Expand Down
6 changes: 3 additions & 3 deletions cookbook/templates/system.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

<h1>{% trans 'System' %}</h1>
{% blocktrans %}
Django Recipes is an open source free software application. It can be found on
<a href="https://github.com/vabene1111/recipes">GitHub</a>.
Changelogs can be found <a href="https://github.com/vabene1111/recipes/releases">here</a>.
Tandoor Recipes is an open source free software application. It can be found on
<a href="https://github.com/TandoorRecipes/recipes">GitHub</a>.
Changelogs can be found <a href="https://github.com/TandoorRecipes/recipes/releases">here</a>.
{% endblocktrans %}

<h3 class="mt-5">{% trans 'System Information' %}</h3>
Expand Down
13 changes: 11 additions & 2 deletions cookbook/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1455,13 +1455,21 @@ def post(self, request, *args, **kwargs):

url = serializer.validated_data.get('url', None)
data = unquote(serializer.validated_data.get('data', None))

duplicate = False
if url:
# Check for existing recipes with provided url
existing_recipe = Recipe.objects.filter(source_url=url).first()
if existing_recipe:
duplicate = True

if not url and not data:
return Response({'error': True, 'msg': _('Nothing to do.')}, status=status.HTTP_400_BAD_REQUEST)

elif url and not data:
if re.match('^(https?://)?(www\\.youtube\\.com|youtu\\.be)/.+$', url):
if validate_import_url(url):
return Response({'recipe_json': get_from_youtube_scraper(url, request), 'recipe_images': [], }, status=status.HTTP_200_OK)
return Response({'recipe_json': get_from_youtube_scraper(url, request), 'recipe_images': [], 'duplicate': duplicate}, status=status.HTTP_200_OK)
if re.match('^(.)*/view/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
recipe_json = requests.get(
url.replace('/view/recipe/', '/api/recipe/').replace(re.split('/view/recipe/[0-9]+', url)[1], '') + '?share='
Expand All @@ -1476,7 +1484,7 @@ def post(self, request, *args, **kwargs):
filetype=pathlib.Path(recipe_json['image']).suffix),
name=f'{uuid.uuid4()}_{recipe.pk}{pathlib.Path(recipe_json["image"]).suffix}')
recipe.save()
return Response({'link': request.build_absolute_uri(reverse('view_recipe', args={recipe.pk}))}, status=status.HTTP_201_CREATED)
return Response({'link': request.build_absolute_uri(reverse('view_recipe', args={recipe.pk})), 'duplicate': duplicate}, status=status.HTTP_201_CREATED)
else:
try:
if validate_import_url(url):
Expand Down Expand Up @@ -1511,6 +1519,7 @@ def post(self, request, *args, **kwargs):
return Response({
'recipe_json': helper.get_from_scraper(scrape, request),
'recipe_images': list(dict.fromkeys(get_images_from_soup(scrape.soup, url))),
'duplicate': duplicate
},
status=status.HTTP_200_OK)

Expand Down
25 changes: 16 additions & 9 deletions docs/features/authentication.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Besides the normal django username and password authentication this application supports multiple
Besides the normal django username and password authentication this application supports multiple
methods of central account management and authentication.

## Allauth
[Django Allauth](https://django-allauth.readthedocs.io/en/latest/index.html) is an awesome project that
[Django Allauth](https://django-allauth.readthedocs.io/en/latest/index.html) is an awesome project that
allows you to use a [huge number](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) of different
authentication providers.

Expand All @@ -11,8 +11,8 @@ They basically explain everything in their documentation, but the following is a
!!! warning "Public Providers"
If you choose Google, Github or any other publicly available service as your authentication provider anyone
with an account on that site can create an account on your installation.
A new account does not have any permission but it is still **not recommended** to give public access to
your installation.
A new account does not have any permission but it is still **not recommended** to give public access to
your installation.

Choose a provider from the [list](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) and install it using the environment variable `SOCIAL_PROVIDERS` as shown
in the example below.
Expand All @@ -28,15 +28,15 @@ SOCIAL_PROVIDERS=allauth.socialaccount.providers.openid_connect,allauth.socialac

### Configuration, via environment

Depending on your authentication provider you **might need** to configure it.
This needs to be done through the settings system. To make the system flexible (allow multiple providers) and to
Depending on your authentication provider you **might need** to configure it.
This needs to be done through the settings system. To make the system flexible (allow multiple providers) and to
not require another file to be mounted into the container the configuration ins done through a single
environment variable. The downside of this approach is that the configuration needs to be put into a single line
as environment files loaded by docker compose don't support multiple lines for a single variable.

The line data needs to either be in json or as Python dictionary syntax.

Take the example configuration from the allauth docs, fill in your settings and then inline the whole object
Take the example configuration from the allauth docs, fill in your settings and then inline the whole object
(you can use a service like [www.freeformatter.com](https://www.freeformatter.com/json-formatter.html) for formatting).
Assign it to the additional `SOCIALACCOUNT_PROVIDERS` variable.

Expand All @@ -46,6 +46,13 @@ The example below is for a generic OIDC provider with PKCE enabled. Most values
SOCIALACCOUNT_PROVIDERS = "{ 'openid_connect': { 'OAUTH_PKCE_ENABLED': True, 'APPS': [ { 'provider_id': 'oidc', 'name': 'My-IDM', 'client_id': 'my_client_id', 'secret': 'my_client_secret', 'settings': { 'server_url': 'https://idm.example.com/oidc/recipes' } } ] } }"
```

Because this JSON contains sensitive data (client id and secret), you may instead choose to save the JSON in a file
and set the environment variable `SOCIALACCOUNT_PROVIDERS_FILE` to the path of the file containing the JSON.

```
SOCIALACCOUNT_PROVIDERS_FILE=/run/secrets/socialaccount_providers.txt
```

!!! success "Improvements ?"
There are most likely ways to achieve the same goal but with a cleaner or simpler system.
If you know such a way feel free to let me know.
Expand Down Expand Up @@ -81,7 +88,7 @@ SOCIALACCOUNT_PROVIDERS='{"openid_connect":{"APPS":[{"provider_id":"keycloak","n
You are now able to sign in using Keycloak after a restart of the service.

### Linking accounts
To link an account to an already existing normal user go to the settings page of the user and link it.
To link an account to an already existing normal user go to the settings page of the user and link it.
Here you can also unlink your account if you no longer want to use a social login method.

## LDAP
Expand Down Expand Up @@ -111,7 +118,7 @@ AUTH_LDAP_TLS_CACERTFILE=/etc/ssl/certs/own-ca.pem
If you just set `REMOTE_USER_AUTH=1` without any additional configuration, _anybody_ can authenticate with _any_ username!

!!! Info "Community Contributed Tutorial"
This tutorial was provided by a community member. We are not able to provide any support! Please only use, if you know what you are doing!
This tutorial was provided by a community member. We are not able to provide any support! Please only use, if you know what you are doing!

In order use external authentication (i.e. using a proxy auth like Authelia, Authentik, etc.) you will need to:

Expand Down
2 changes: 1 addition & 1 deletion docs/install/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
These instructions are inspired from a standard django/gunicorn/postgresql instructions ([for example](https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-16-04))

!!! warning
Make sure to use Python 3.10 or higher, and ensure that `pip` is associated with Python 3. Depending on your system configuration, using `python` or `pip` might default to Python 2. Make sure your machine has at least 2048 MB of memory; otherwise, the `yarn build` process may fail with the error: `FATAL ERROR: Reached heap limit - Allocation failed: JavaScript heap out of memory`.
Make sure to use at least Python 3.10 (although 3.12 is preferred) or higher, and ensure that `pip` is associated with Python 3. Depending on your system configuration, using `python` or `pip` might default to Python 2. Make sure your machine has at least 2048 MB of memory; otherwise, the `yarn build` process may fail with the error: `FATAL ERROR: Reached heap limit - Allocation failed: JavaScript heap out of memory`.

## Prerequisites

Expand Down
Loading

0 comments on commit 954a35b

Please sign in to comment.