mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-10-26 08:39:00 +00:00
Build and push containers / metadata (push) Canceled after 0s
Build and push containers / build-push-containers (push) Canceled after 0s
ChangeDetection.io Container Build Test / Build linux/amd64 (alpine) (push) Canceled after 0s
ChangeDetection.io Container Build Test / Build linux/arm64 (alpine) (push) Canceled after 0s
ChangeDetection.io Container Build Test / Build linux/amd64 (main) (push) Canceled after 0s
ChangeDetection.io Container Build Test / Build linux/arm/v7 (main) (push) Canceled after 0s
ChangeDetection.io Container Build Test / Build linux/arm/v8 (main) (push) Canceled after 0s
ChangeDetection.io Container Build Test / Build linux/arm64 (main) (push) Canceled after 0s
ChangeDetection.io App Test / lint-code (push) Canceled after 0s
ChangeDetection.io App Test / lint-translations (push) Canceled after 0s
ChangeDetection.io App Test / lint-template-i18n (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-11 (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-12 (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-13 (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-14 (push) Canceled after 0s
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Canceled after 0s
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Canceled after 0s
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Canceled after 0s
* API - Reject broken Jinja2 in url, link_to_open, body and headers, shared validation with the edit form
The edit form validated Jinja2 templates on save but the API did not, so a broken body or
header template saved through the API only failed at fetch time, on every check.
- New jinja2_custom/validate.py: validate_jinja2_fields() renders each value that matches
JINJA2_MARKER_PATTERN and returns {field: translated error}
- The edit form uses it instead of four copies of the same try/except
- API create and update return 400 naming the field
Translations: messages moved to the new file (msgids unchanged); also translated the header
error for fr/it and fixed the cs/es wording.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* rebuild
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
53 lines
2.2 KiB
Python
53 lines
2.2 KiB
Python
"""
|
|
Validate the Jinja2 templates a user can put in a watch's URL, "Link to Open", request body and headers.
|
|
|
|
Shared by the edit form and the API, so both reject the same broken templates on save instead of
|
|
letting the watch fail on every check. Only values that look like Jinja2 (JINJA2_MARKER_PATTERN) are
|
|
rendered, the same rule the fetch uses, so literal text such as URL-encoded JSON ({%22) is left alone.
|
|
"""
|
|
from loguru import logger
|
|
|
|
from . import JINJA2_MARKER_PATTERN, render as jinja_render
|
|
|
|
|
|
def _render_exception(value):
|
|
"""Return the exception raised rendering value as a Jinja2 template, or None if it is fine or not a template."""
|
|
if not isinstance(value, str) or not JINJA2_MARKER_PATTERN.search(value):
|
|
return None
|
|
try:
|
|
jinja_render(template_str=value)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
return e
|
|
return None
|
|
|
|
|
|
def validate_jinja2_fields(url=None, link_to_open=None, body=None, headers=None) -> dict:
|
|
"""Render every templated value and collect the failures.
|
|
|
|
Returns {field_name: translated error message}, keyed by the watch field name, empty when everything is valid.
|
|
"""
|
|
from flask_babel import gettext
|
|
|
|
errors = {}
|
|
|
|
for field_name, value in (('url', url), ('link_to_open', link_to_open), ('body', body)):
|
|
e = _render_exception(value)
|
|
if isinstance(e, ModuleNotFoundError):
|
|
# incase jinja2_time or others is missing
|
|
errors[field_name] = gettext('Invalid template syntax configuration: %(error)s') % {'error': e}
|
|
elif e:
|
|
errors[field_name] = gettext('Invalid template syntax: %(error)s') % {'error': e}
|
|
|
|
for header, value in (headers or {}).items():
|
|
e = _render_exception(value)
|
|
if isinstance(e, ModuleNotFoundError):
|
|
errors['headers'] = gettext('Invalid template syntax configuration: %(error)s') % {'error': e}
|
|
elif e:
|
|
errors['headers'] = gettext('Invalid template syntax in \"%(header)s\" header: %(error)s') % {'header': header, 'error': e}
|
|
if e:
|
|
# Report the first broken header only, by name, so the user knows which one to fix
|
|
break
|
|
|
|
return errors
|