You've already forked snikket-web-portal
Bump quart to version 0.17
This is needed because jinja2 had an update which caused the portal to
not work at all:
```
ImportError: cannot import name 'escape' from 'jinja2'
```
Quart needed updating for that.
This update required a lot of typefixes. Apparently, the "canned"
responses (like redirect) are now plain werkzeug responses, while
quart.Response does not inherit from werkzeug.Response (otherwise, we
could've changed the type annotations to werkzeug.Response everywhere,
but that doesn't work because a quart.Response is not a
werkzeug.Response).
P.S.: This time, I *did* check that avatar uploads don't break (see
b007afc).
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
aiohttp~=3.6
|
||||
quart~=0.11,<0.15
|
||||
quart~=0.17
|
||||
flask-wtf~=0.14
|
||||
hsluv~=0.0.2
|
||||
flask-babel~=1.0
|
||||
|
||||
@@ -18,6 +18,8 @@ from quart import (
|
||||
jsonify,
|
||||
)
|
||||
|
||||
import werkzeug.exceptions
|
||||
|
||||
import environ
|
||||
|
||||
from . import colour, infra
|
||||
@@ -40,7 +42,7 @@ async def proc() -> typing.Dict[str, typing.Any]:
|
||||
|
||||
try:
|
||||
user_info = await infra.client.get_user_info()
|
||||
except (aiohttp.ClientError, quart.exceptions.HTTPException):
|
||||
except (aiohttp.ClientError, werkzeug.exceptions.HTTPException):
|
||||
user_info = {}
|
||||
|
||||
return {
|
||||
@@ -105,16 +107,16 @@ async def backend_error_handler(exc: Exception) -> quart.Response:
|
||||
|
||||
|
||||
async def generic_http_error(
|
||||
exc: quart.exceptions.HTTPException,
|
||||
exc: werkzeug.exceptions.HTTPException,
|
||||
) -> quart.Response:
|
||||
return quart.Response(
|
||||
await render_template(
|
||||
"generic_http_error.html",
|
||||
status=exc.status_code,
|
||||
status=exc.code,
|
||||
description=exc.description,
|
||||
name=exc.name,
|
||||
),
|
||||
status=exc.status_code,
|
||||
status=exc.code,
|
||||
)
|
||||
|
||||
|
||||
@@ -200,19 +202,19 @@ def create_app() -> quart.Quart:
|
||||
app.context_processor(proc)
|
||||
app.register_error_handler(
|
||||
aiohttp.ClientConnectorError,
|
||||
backend_error_handler, # type:ignore
|
||||
backend_error_handler,
|
||||
)
|
||||
app.register_error_handler(
|
||||
quart.exceptions.HTTPException,
|
||||
werkzeug.exceptions.HTTPException,
|
||||
generic_http_error, # type:ignore
|
||||
)
|
||||
app.register_error_handler(
|
||||
Exception,
|
||||
generic_error_handler, # type:ignore
|
||||
generic_error_handler,
|
||||
)
|
||||
|
||||
@app.route("/")
|
||||
async def index() -> quart.Response:
|
||||
async def index() -> werkzeug.Response:
|
||||
if infra.client.has_session:
|
||||
return redirect(url_for('user.index'))
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ from datetime import datetime
|
||||
|
||||
import aiohttp
|
||||
|
||||
import werkzeug.exceptions
|
||||
|
||||
import quart.flask_patch
|
||||
|
||||
import wtforms
|
||||
@@ -92,7 +94,7 @@ class EditUserForm(BaseForm):
|
||||
|
||||
@bp.route("/user/<localpart>/", methods=["GET", "POST"])
|
||||
@client.require_admin_session()
|
||||
async def edit_user(localpart: str) -> typing.Union[quart.Response, str]:
|
||||
async def edit_user(localpart: str) -> typing.Union[werkzeug.Response, str]:
|
||||
target_user_info = await client.get_user_by_localpart(localpart)
|
||||
|
||||
form = EditUserForm()
|
||||
@@ -147,7 +149,7 @@ class DeleteUserForm(BaseForm):
|
||||
|
||||
@bp.route("/user/<localpart>/delete", methods=["GET", "POST"])
|
||||
@client.require_admin_session()
|
||||
async def delete_user(localpart: str) -> typing.Union[str, quart.Response]:
|
||||
async def delete_user(localpart: str) -> typing.Union[str, werkzeug.Response]:
|
||||
target_user_info = await client.get_user_by_localpart(localpart)
|
||||
form = DeleteUserForm()
|
||||
if form.validate_on_submit():
|
||||
@@ -186,7 +188,7 @@ async def debug_user(localpart: str) -> typing.Union[str, quart.Response]:
|
||||
@client.require_admin_session()
|
||||
async def user_password_reset_link(
|
||||
id_: str,
|
||||
) -> typing.Union[str, quart.Response]:
|
||||
) -> typing.Union[str, werkzeug.Response]:
|
||||
invite_info = await client.get_invite_by_id(
|
||||
id_,
|
||||
)
|
||||
@@ -278,7 +280,7 @@ class InvitePost(BaseForm):
|
||||
|
||||
@bp.route("/invitations", methods=["GET", "POST"])
|
||||
@client.require_admin_session()
|
||||
async def invitations() -> typing.Union[str, quart.Response]:
|
||||
async def invitations() -> typing.Union[str, werkzeug.Response]:
|
||||
invites = sorted(
|
||||
(
|
||||
invite
|
||||
@@ -324,7 +326,7 @@ class InviteForm(BaseForm):
|
||||
|
||||
@bp.route("/invitation/-/new", methods=["POST"])
|
||||
@client.require_admin_session()
|
||||
async def create_invite() -> typing.Union[str, quart.Response]:
|
||||
async def create_invite() -> typing.Union[str, werkzeug.Response]:
|
||||
form = InvitePost()
|
||||
circles = await client.list_groups()
|
||||
form.circles.choices = [
|
||||
@@ -352,7 +354,7 @@ async def create_invite() -> typing.Union[str, quart.Response]:
|
||||
|
||||
@bp.route("/invitation/<id_>", methods=["GET", "POST"])
|
||||
@client.require_admin_session()
|
||||
async def edit_invite(id_: str) -> typing.Union[str, quart.Response]:
|
||||
async def edit_invite(id_: str) -> typing.Union[str, werkzeug.Response]:
|
||||
try:
|
||||
invite_info = await client.get_invite_by_id(id_)
|
||||
except aiohttp.ClientResponseError as exc:
|
||||
@@ -418,7 +420,7 @@ async def circles() -> str:
|
||||
|
||||
@bp.route("/circle/-/new", methods=["POST"])
|
||||
@client.require_admin_session()
|
||||
async def create_circle() -> typing.Union[str, quart.Response]:
|
||||
async def create_circle() -> typing.Union[str, werkzeug.Response]:
|
||||
create_form = CirclePost()
|
||||
if create_form.validate_on_submit():
|
||||
circle = await client.create_group(
|
||||
@@ -464,7 +466,7 @@ class EditCircleForm(BaseForm):
|
||||
|
||||
@bp.route("/circle/<id_>", methods=["GET", "POST"])
|
||||
@client.require_admin_session()
|
||||
async def edit_circle(id_: str) -> typing.Union[str, quart.Response]:
|
||||
async def edit_circle(id_: str) -> typing.Union[str, werkzeug.Response]:
|
||||
async with client.authenticated_session() as session:
|
||||
try:
|
||||
circle = await client.get_group_by_id(
|
||||
@@ -626,7 +628,7 @@ class AnnouncementForm(BaseForm):
|
||||
|
||||
@bp.route("/system/", methods=["GET", "POST"])
|
||||
@client.require_admin_session()
|
||||
async def system() -> typing.Union[str, quart.Response]:
|
||||
async def system() -> typing.Union[str, werkzeug.Response]:
|
||||
form = AnnouncementForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
@@ -657,7 +659,7 @@ async def system() -> typing.Union[str, quart.Response]:
|
||||
now = time.time()
|
||||
try:
|
||||
prosody_metrics = await client.get_system_metrics()
|
||||
except quart.exceptions.NotFound:
|
||||
except werkzeug.exceptions.NotFound:
|
||||
# server does not offer the endpoint for whatever reason -- ignore
|
||||
prosody_metrics = {}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ from quart import (
|
||||
session as http_session,
|
||||
)
|
||||
|
||||
import werkzeug
|
||||
|
||||
import wtforms
|
||||
|
||||
from flask_babel import lazy_gettext as _l, gettext
|
||||
@@ -46,14 +48,14 @@ def apple_store_badge() -> str:
|
||||
|
||||
|
||||
@bp.context_processor
|
||||
def context() -> typing.Mapping[str, typing.Any]:
|
||||
def context() -> typing.Dict[str, typing.Any]:
|
||||
return {
|
||||
"apple_store_badge": apple_store_badge,
|
||||
}
|
||||
|
||||
|
||||
@bp.route("/<id_>")
|
||||
async def view_old(id_: str) -> quart.Response:
|
||||
async def view_old(id_: str) -> werkzeug.Response:
|
||||
return redirect(url_for(".view", id_=id_))
|
||||
|
||||
|
||||
@@ -131,7 +133,7 @@ class RegisterForm(BaseForm):
|
||||
|
||||
|
||||
@bp.route("/<id_>/register", methods=["GET", "POST"])
|
||||
async def register(id_: str) -> typing.Union[str, quart.Response]:
|
||||
async def register(id_: str) -> typing.Union[str, werkzeug.Response]:
|
||||
try:
|
||||
invite = await client.get_public_invite_by_id(id_)
|
||||
except aiohttp.ClientResponseError as exc:
|
||||
@@ -199,7 +201,7 @@ class ResetForm(BaseForm):
|
||||
|
||||
|
||||
@bp.route("/<id_>/reset", methods=["GET", "POST"])
|
||||
async def reset(id_: str) -> typing.Union[str, quart.Response]:
|
||||
async def reset(id_: str) -> typing.Union[str, werkzeug.Response]:
|
||||
try:
|
||||
invite = await client.get_public_invite_by_id(id_)
|
||||
except aiohttp.ClientResponseError as exc:
|
||||
@@ -300,5 +302,5 @@ async def reset_success() -> str:
|
||||
|
||||
|
||||
@bp.route("/-")
|
||||
async def index() -> quart.Response:
|
||||
async def index() -> werkzeug.Response:
|
||||
return redirect(url_for("index"))
|
||||
|
||||
@@ -18,6 +18,8 @@ from quart import (
|
||||
flash,
|
||||
)
|
||||
|
||||
import werkzeug.exceptions
|
||||
|
||||
import babel
|
||||
import wtforms
|
||||
|
||||
@@ -48,7 +50,7 @@ class LoginForm(BaseForm):
|
||||
|
||||
|
||||
@bp.route("/-")
|
||||
async def index() -> quart.Response:
|
||||
async def index() -> werkzeug.Response:
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@@ -56,7 +58,7 @@ ERR_CREDENTIALS_INVALID = _l("Invalid username or password.")
|
||||
|
||||
|
||||
@bp.route("/login", methods=["GET", "POST"])
|
||||
async def login() -> typing.Union[str, quart.Response]:
|
||||
async def login() -> typing.Union[str, werkzeug.Response]:
|
||||
if client.has_session and (await client.test_session()):
|
||||
return redirect(url_for('user.index'))
|
||||
|
||||
@@ -76,7 +78,7 @@ async def login() -> typing.Union[str, quart.Response]:
|
||||
password = form.password.data
|
||||
try:
|
||||
await client.login(jid, password)
|
||||
except quart.exceptions.Unauthorized:
|
||||
except werkzeug.exceptions.Unauthorized:
|
||||
form.password.errors.append(ERR_CREDENTIALS_INVALID)
|
||||
else:
|
||||
await flash(
|
||||
@@ -95,14 +97,13 @@ async def about() -> str:
|
||||
|
||||
if current_app.debug or client.is_admin_session:
|
||||
version = _version.version
|
||||
extra_versions["Quart"] = quart.__version__
|
||||
extra_versions["aiohttp"] = aiohttp.__version__
|
||||
extra_versions["babel"] = babel.__version__
|
||||
extra_versions["wtforms"] = wtforms.__version__
|
||||
extra_versions["flask-wtf"] = flask_wtf.__version__
|
||||
try:
|
||||
extra_versions["Prosody"] = await client.get_server_version()
|
||||
except quart.exceptions.Unauthorized:
|
||||
except werkzeug.exceptions.Unauthorized:
|
||||
extra_versions["Prosody"] = "unknown"
|
||||
|
||||
return await render_template(
|
||||
|
||||
@@ -19,7 +19,9 @@ from quart import (
|
||||
current_app, _app_ctx_stack, session as http_session, abort, redirect,
|
||||
url_for,
|
||||
)
|
||||
import quart.exceptions
|
||||
import quart
|
||||
|
||||
import werkzeug.exceptions
|
||||
|
||||
from . import xmpputil
|
||||
from .xmpputil import split_jid
|
||||
@@ -386,16 +388,16 @@ class ProsodyClient:
|
||||
) -> typing.Callable[
|
||||
[typing.Callable[..., typing.Awaitable[T]]],
|
||||
typing.Callable[..., typing.Awaitable[
|
||||
typing.Union[T, quart.Response]]]]:
|
||||
typing.Union[T, quart.Response, werkzeug.Response]]]]:
|
||||
def decorator(
|
||||
f: typing.Callable[..., typing.Awaitable[T]],
|
||||
) -> typing.Callable[..., typing.Awaitable[
|
||||
typing.Union[T, quart.Response]]]:
|
||||
typing.Union[T, quart.Response, werkzeug.Response]]]:
|
||||
@functools.wraps(f)
|
||||
async def wrapped(
|
||||
*args: typing.Any,
|
||||
**kwargs: typing.Any,
|
||||
) -> typing.Union[T, quart.Response]:
|
||||
) -> typing.Union[T, quart.Response, werkzeug.Response]:
|
||||
if not self.has_session or not (await self.test_session()):
|
||||
redirect_to_value = redirect_to
|
||||
if redirect_to_value is not False:
|
||||
@@ -415,17 +417,17 @@ class ProsodyClient:
|
||||
) -> typing.Callable[
|
||||
[typing.Callable[..., typing.Awaitable[T]]],
|
||||
typing.Callable[..., typing.Awaitable[
|
||||
typing.Union[T, quart.Response]]]]:
|
||||
typing.Union[T, quart.Response, werkzeug.Response]]]]:
|
||||
def decorator(
|
||||
f: typing.Callable[..., typing.Awaitable[T]],
|
||||
) -> typing.Callable[..., typing.Awaitable[
|
||||
typing.Union[T, quart.Response]]]:
|
||||
typing.Union[T, quart.Response, werkzeug.Response]]]:
|
||||
@functools.wraps(f)
|
||||
@self.require_session(redirect_to=redirect_to)
|
||||
async def wrapped(
|
||||
*args: typing.Any,
|
||||
**kwargs: typing.Any,
|
||||
) -> typing.Union[T, quart.Response]:
|
||||
) -> typing.Union[T, quart.Response, werkzeug.Response]:
|
||||
if not self.is_admin_session:
|
||||
raise abort(403, "This is not for you.")
|
||||
|
||||
@@ -492,7 +494,7 @@ class ProsodyClient:
|
||||
session=session,
|
||||
)
|
||||
avatar_hash = avatar_info["sha1"]
|
||||
except quart.exceptions.HTTPException:
|
||||
except werkzeug.exceptions.HTTPException:
|
||||
avatar_hash = None
|
||||
|
||||
return {
|
||||
@@ -644,7 +646,7 @@ class ProsodyClient:
|
||||
new_access_model,
|
||||
)
|
||||
))
|
||||
except quart.exceptions.NotFound:
|
||||
except werkzeug.exceptions.NotFound:
|
||||
if ignore_not_found:
|
||||
return
|
||||
raise
|
||||
@@ -774,7 +776,7 @@ class ProsodyClient:
|
||||
session: aiohttp.ClientSession,
|
||||
) -> str:
|
||||
access_models = filter(
|
||||
lambda x: not isinstance(x, quart.exceptions.NotFound),
|
||||
lambda x: not isinstance(x, werkzeug.exceptions.NotFound),
|
||||
await asyncio.gather(
|
||||
self.get_avatar_access_model(session=session),
|
||||
self.get_nickname_access_model(session=session),
|
||||
|
||||
@@ -13,7 +13,7 @@ from quart import (
|
||||
flash,
|
||||
current_app,
|
||||
)
|
||||
import quart.exceptions
|
||||
import werkzeug.exceptions
|
||||
|
||||
import wtforms
|
||||
|
||||
@@ -96,7 +96,7 @@ async def index() -> str:
|
||||
|
||||
@bp.route('/passwd', methods=["GET", "POST"])
|
||||
@client.require_session()
|
||||
async def change_pw() -> typing.Union[str, quart.Response]:
|
||||
async def change_pw() -> typing.Union[str, werkzeug.Response]:
|
||||
form = ChangePasswordForm()
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
@@ -104,8 +104,8 @@ async def change_pw() -> typing.Union[str, quart.Response]:
|
||||
form.current_password.data,
|
||||
form.new_password.data,
|
||||
)
|
||||
except (quart.exceptions.Unauthorized,
|
||||
quart.exceptions.Forbidden):
|
||||
except (werkzeug.exceptions.Unauthorized,
|
||||
werkzeug.exceptions.Forbidden):
|
||||
# server refused current password, set an appropriate error
|
||||
form.current_password.errors.append(
|
||||
_("Incorrect password."),
|
||||
@@ -128,7 +128,7 @@ EAVATARTOOBIG = _l(
|
||||
|
||||
@bp.route("/profile", methods=["GET", "POST"])
|
||||
@client.require_session()
|
||||
async def profile() -> typing.Union[str, quart.Response]:
|
||||
async def profile() -> typing.Union[str, werkzeug.Response]:
|
||||
max_avatar_size = current_app.config["MAX_AVATAR_SIZE"]
|
||||
|
||||
form = ProfileForm()
|
||||
@@ -221,7 +221,7 @@ async def manage_data() -> typing.Union[str, quart.Response]:
|
||||
|
||||
@bp.route("/logout", methods=["GET", "POST"])
|
||||
@client.require_session()
|
||||
async def logout() -> typing.Union[quart.Response, str]:
|
||||
async def logout() -> typing.Union[werkzeug.Response, str]:
|
||||
form = LogoutForm()
|
||||
if form.validate_on_submit():
|
||||
await client.logout()
|
||||
|
||||
@@ -4,7 +4,7 @@ import typing
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from quart import abort
|
||||
import quart.exceptions
|
||||
import werkzeug.exceptions
|
||||
|
||||
|
||||
TAG_XMPP_ERROR = "error"
|
||||
@@ -239,7 +239,7 @@ def extract_pubsub_item_get_reply(
|
||||
) -> typing.Optional[ET.Element]:
|
||||
try:
|
||||
pubsub = extract_iq_reply(iq_tree, TAG_PUBSUB)
|
||||
except quart.exceptions.NotFound:
|
||||
except werkzeug.exceptions.NotFound:
|
||||
return None
|
||||
|
||||
if pubsub is None:
|
||||
|
||||
Reference in New Issue
Block a user