diff options
| author | Dennis Fink | 2026-08-09 14:15:29 +0200 |
|---|---|---|
| committer | Dennis Fink | 2026-08-09 14:22:00 +0200 |
| commit | 67eff0a854da010cb6ecd119d84238ec3e119272 (patch) | |
| tree | c4a85592b957bb01fa62f79329faa6ba0823d9b3 /webmentions_ssg/forms | |
| parent | f53c1136184d2afabfb1e5974e527229aa71939a (diff) | |
| download | webmentions-ssg-67eff0a854da010cb6ecd119d84238ec3e119272.tar.gz webmentions-ssg-67eff0a854da010cb6ecd119d84238ec3e119272.zip | |
Implement received Webmention handling
Add the Flask application setup, database models and migrations,
authentication, and configuration for development and testing.
Implement asynchronous Webmention verification with Huey, including HTML
and plain-text source validation, retries, status tracking, and size
limits.
Add status, login, and paginated received-Webmention views together with
comprehensive tests for forms, views, and receiver tasks.
Diffstat (limited to '')
| -rw-r--r-- | webmentions_ssg/forms/__init__.py | 42 | ||||
| -rw-r--r-- | webmentions_ssg/forms/validators.py | 62 |
2 files changed, 104 insertions, 0 deletions
diff --git a/webmentions_ssg/forms/__init__.py b/webmentions_ssg/forms/__init__.py new file mode 100644 index 0000000..98962ad --- /dev/null +++ b/webmentions_ssg/forms/__init__.py @@ -0,0 +1,42 @@ +import re + +from flask_wtf import FlaskForm +from wtforms import PasswordField, StringField, SubmitField +from wtforms.validators import URL, InputRequired, Regexp + +from .validators import AllowedHostname, NotEqualTo + + +class LoginForm(FlaskForm): + username = StringField("Username", validators=[InputRequired()]) + password = PasswordField("Password", validators=[InputRequired()]) + submit = SubmitField("Sign In") + + +class EndpointForm(FlaskForm): + source = StringField( + "source", + validators=[ + InputRequired(), + URL(), + Regexp( + "^https?://.*", + flags=re.IGNORECASE, + message="source must begin with http or https", + ), + NotEqualTo("target"), + ], + ) + target = StringField( + "target", + validators=[ + InputRequired(), + URL(), + Regexp( + "^https?://.*", + flags=re.IGNORECASE, + message="target must begin with http or https", + ), + AllowedHostname(), + ], + ) diff --git a/webmentions_ssg/forms/validators.py b/webmentions_ssg/forms/validators.py new file mode 100644 index 0000000..20d9ebf --- /dev/null +++ b/webmentions_ssg/forms/validators.py @@ -0,0 +1,62 @@ +from urllib.parse import urlsplit + +from flask import current_app +from wtforms import ValidationError + + +class NotEqualTo: + """ + Compares the values of two fields. + + :param fieldname: + The name of the other field to compare to. + :param message: + Error message to raise in case of a validation error. Can be + interpolated with `%(other_label)s` and `%(other_name)s` to provide a + more helpful error. + """ + + def __init__(self, fieldname, message=None): + self.fieldname = fieldname + self.message = message + + def __call__(self, form, field): + try: + other = form[self.fieldname] + except KeyError as exc: + raise ValidationError( + field.gettext("Invalid field name '%s'.") % self.fieldname + ) from exc + + if field.data != other.data: + return + + d = { + "other_label": hasattr(other, "label") + and other.label.text + or self.fieldname, + "other_name": self.fieldname, + } + message = self.message + if message is None: + message = field.gettext("Field must not be equal to %(other_name)s.") + + raise ValidationError(message % d) + + +class AllowedHostname: + def __init__(self, message=None): + self.message = message + + def __call__(self, form, field): + if ( + urlsplit(field.data).hostname + in current_app.config["WEBMENTIONS_SSG_ALLOWED_HOSTNAMES"] + ): + return + + message = self.message + if self.message is None: + message = field.gettext("Invalid input.") + + raise ValidationError(message) |
