diff --git a/snikket_web/__init__.py b/snikket_web/__init__.py index 6d98bb7..ae77d84 100644 --- a/snikket_web/__init__.py +++ b/snikket_web/__init__.py @@ -5,11 +5,16 @@ import os import pathlib import typing +import aiohttp + import quart.flask_patch import quart from quart import ( url_for, + render_template, + current_app, + redirect, ) import environ @@ -18,7 +23,7 @@ from . import colour, infra from ._version import version, version_info # noqa:F401 -def proc() -> typing.Dict[str, typing.Any]: +async def proc() -> typing.Dict[str, typing.Any]: def url_for_avatar(entity: str, hash_: str, **kwargs: typing.Any) -> str: return url_for( @@ -32,10 +37,16 @@ def proc() -> typing.Dict[str, typing.Any]: **kwargs ) + try: + user_info = await infra.client.get_user_info() + except (aiohttp.ClientError, quart.exceptions.HTTPException): + user_info = {} + return { "url_for_avatar": url_for_avatar, "text_to_css": colour.text_to_css, "lang": infra.selected_locale(), + "user_info": user_info, } @@ -45,6 +56,85 @@ def autosplit(s: typing.Union[str, typing.List[str]]) -> typing.List[str]: return s +async def render_exception_template( + template: str, + exc: Exception, + error_id: str, + ) -> str: + more: typing.Dict[str, str] = {} + if current_app.debug: + import traceback + more.update( + traceback="".join(traceback.format_exception( + type(exc), + exc, + exc.__traceback__, + )), + ) + + return await render_template( + template, + exception_short=str( + ".".join([ + type(exc).__module__, + type(exc).__qualname__, + ]), + ), + error_id=error_id, + **more, + ) + + +async def backend_error_handler(exc: Exception) -> quart.Response: + error_id = infra.generate_error_id() + current_app.logger.error( + "error_id=%s returning 503 status page for exception", + error_id, + exc_info=exc, + ) + return quart.Response( + await render_exception_template( + "backend_error.html", + exc, + error_id, + ), + status=503, + ) + + +async def generic_http_error( + exc: quart.exceptions.HTTPException, + ) -> quart.Response: + return quart.Response( + await render_template( + "generic_http_error.html", + status=exc.status_code, + description=exc.description, + name=exc.name, + ), + status=exc.status_code, + ) + + +async def generic_error_handler( + exc: Exception, + ) -> quart.Response: + error_id = infra.generate_error_id() + current_app.logger.error( + "error_id=%s returning 500 status page for exception", + error_id, + exc_info=exc, + ) + return quart.Response( + await render_exception_template( + "internal_error.html", + exc, + error_id, + ), + status=500, + ) + + @environ.config(prefix="SNIKKET_WEB") class AppConfig: secret_key = environ.var() @@ -82,6 +172,25 @@ def create_app() -> quart.Quart: app.config["AVATAR_CACHE_TTL"] = config.avatar_cache_ttl app.context_processor(proc) + app.register_error_handler( + aiohttp.ClientConnectorError, + backend_error_handler, # type:ignore + ) + app.register_error_handler( + quart.exceptions.HTTPException, + generic_http_error, # type:ignore + ) + app.register_error_handler( + Exception, + generic_error_handler, # type:ignore + ) + + @app.route("/") + async def index() -> quart.Response: + if infra.client.has_session: + return redirect(url_for('user.index')) + + return redirect(url_for('main.login')) logging_config = app.config.get("LOGGING_CONFIG") if logging_config is not None: diff --git a/snikket_web/admin.py b/snikket_web/admin.py index dd2dc96..e6ab070 100644 --- a/snikket_web/admin.py +++ b/snikket_web/admin.py @@ -30,14 +30,12 @@ bp = Blueprint("admin", __name__, url_prefix="/admin") @bp.route("/") @client.require_admin_session() async def index() -> str: - user_info = await client.get_user_info() - return await render_template("admin_home.html", user_info=user_info) + return await render_template("admin_home.html") @bp.route("/users") @client.require_admin_session() async def users() -> str: - user_info = await client.get_user_info() users = sorted( await client.list_users(), key=lambda x: x.localpart @@ -45,7 +43,6 @@ async def users() -> str: return await render_template( "admin_users.html", users=users, - user_info=user_info, ) @@ -58,7 +55,6 @@ class DeleteUserForm(flask_wtf.FlaskForm): # type:ignore @bp.route("/user//delete", methods=["GET", "POST"]) @client.require_admin_session() async def delete_user(localpart: str) -> typing.Union[str, quart.Response]: - user_info = await client.get_user_info() target_user_info = await client.get_user_by_localpart(localpart) form = DeleteUserForm() if form.validate_on_submit(): @@ -69,7 +65,6 @@ async def delete_user(localpart: str) -> typing.Union[str, quart.Response]: return await render_template( "admin_delete_user.html", target_user=target_user_info, - user_info=user_info, form=form, ) @@ -130,7 +125,6 @@ class InvitePost(flask_wtf.FlaskForm): # type:ignore @bp.route("/invitations", methods=["GET", "POST"]) @client.require_admin_session() async def invitations() -> typing.Union[str, quart.Response]: - user_info = await client.get_user_info() invites = sorted( await client.list_invites(), key=lambda x: x.created_at, @@ -156,7 +150,6 @@ async def invitations() -> typing.Union[str, quart.Response]: return await render_template( "admin_invites.html", - user_info=user_info, invites=invites, invite_form=invite_form, now=datetime.utcnow(), @@ -174,7 +167,6 @@ class InviteForm(flask_wtf.FlaskForm): # type:ignore @bp.route("/invitation/-/new", methods=["POST"]) @client.require_admin_session() async def create_invite() -> typing.Union[str, quart.Response]: - user_info = await client.get_user_info() form = InvitePost() circles = await client.list_groups() form.circles.choices = [ @@ -188,14 +180,12 @@ async def create_invite() -> typing.Union[str, quart.Response]: ) return redirect(url_for(".edit_invite", id_=invite.id_)) return await render_template("admin_create_invite.html", - user_info=user_info, invite_form=form) @bp.route("/invitation/", methods=["GET", "POST"]) @client.require_admin_session() async def edit_invite(id_: str) -> typing.Union[str, quart.Response]: - user_info = await client.get_user_info() invite_info = await client.get_invite_by_id(id_) circles = await client.list_groups() circle_map = { @@ -212,7 +202,6 @@ async def edit_invite(id_: str) -> typing.Union[str, quart.Response]: return await render_template( "admin_edit_invite.html", - user_info=user_info, invite=invite_info, now=datetime.utcnow(), form=form, @@ -233,7 +222,6 @@ class CirclePost(flask_wtf.FlaskForm): # type:ignore @bp.route("/circles") @client.require_admin_session() async def circles() -> str: - user_info = await client.get_user_info() circles = sorted( await client.list_groups(), key=lambda x: x.name @@ -243,7 +231,6 @@ async def circles() -> str: return await render_template( "admin_circles.html", circles=circles, - user_info=user_info, invite_form=invite_form, create_form=create_form, ) @@ -252,7 +239,6 @@ async def circles() -> str: @bp.route("/circle/-/new", methods=["POST"]) @client.require_admin_session() async def create_circle() -> typing.Union[str, quart.Response]: - user_info = await client.get_user_info() create_form = CirclePost() if create_form.validate_on_submit(): circle = await client.create_group( @@ -262,7 +248,6 @@ async def create_circle() -> typing.Union[str, quart.Response]: return await render_template( "admin_create_circle.html", - user_info=user_info, create_form=create_form, ) @@ -287,9 +272,6 @@ class EditCircleForm(flask_wtf.FlaskForm): # type:ignore @client.require_admin_session() async def edit_circle(id_: str) -> typing.Union[str, quart.Response]: async with client.authenticated_session() as session: - user_info = await client.get_user_info( - session=session, - ) try: circle = await client.get_group_by_id( id_, @@ -328,7 +310,6 @@ async def edit_circle(id_: str) -> typing.Union[str, quart.Response]: return await render_template( "admin_edit_circle.html", target_circle=circle, - user_info=user_info, form=form, circle_members=circle_members, invite_form=invite_form, diff --git a/snikket_web/infra.py b/snikket_web/infra.py index 7021c16..22f571f 100644 --- a/snikket_web/infra.py +++ b/snikket_web/infra.py @@ -1,4 +1,6 @@ +import base64 import itertools +import secrets import typing import quart.flask_patch # noqa:F401 @@ -48,3 +50,9 @@ def init_templating(app: quart.Quart) -> None: app.template_filter("format_timedelta")(flask_babel.format_timedelta) app.template_filter("flatten")(flatten) app.template_filter("circle_name")(circle_name) + + +def generate_error_id() -> str: + return base64.b32encode(secrets.token_bytes(8)).decode( + "ascii" + ).rstrip("=") diff --git a/snikket_web/scss/app.scss b/snikket_web/scss/app.scss index 98fd303..51110f1 100644 --- a/snikket_web/scss/app.scss +++ b/snikket_web/scss/app.scss @@ -914,3 +914,11 @@ ul.inline { body.no-copy .copy-to-clipboard { display: none !important; } + + +/* magic */ + +pre.guru-meditation { + width: 100%; + overflow-x: scroll; +} diff --git a/snikket_web/templates/app.html b/snikket_web/templates/app.html index 65e7779..e830f61 100644 --- a/snikket_web/templates/app.html +++ b/snikket_web/templates/app.html @@ -1,22 +1,9 @@ -{% extends "base.html" %} +{% extends "unauth.html" %} {% from "library.j2" import avatar with context %} {% block head_lead %} {% trans %}Snikket Web Portal{% endtrans %} {% endblock %} -{% block style %} - -{{ super() }} -{% endblock %} -{% block body %} -
-
{{ config["SNIKKET_DOMAIN"] }}
- {% block topbar_left %}{% endblock %} -
- {% block topbar_right %}{% endblock %} - -
-
{% block content %}{% endblock %}
-
-
  • {% trans about_url=url_for('main.about') %}A Snikket server{% endtrans %}
-
-{% endblock %} +{% block topbar_right %} +{{- super() -}} + +{%- endblock %} diff --git a/snikket_web/templates/backend_error.html b/snikket_web/templates/backend_error.html new file mode 100644 index 0000000..23e684f --- /dev/null +++ b/snikket_web/templates/backend_error.html @@ -0,0 +1,6 @@ +{% extends "exception.html" %} +{% block box -%} +
{% trans %}Internal error{% endtrans %}
+

{% trans %}The web portal was not able to communicate with the backend.{% endtrans %}

+

{% trans %}Please try again later and/or inform your Snikket instance admin.{% endtrans %}

+{%- endblock %} diff --git a/snikket_web/templates/exception.html b/snikket_web/templates/exception.html new file mode 100644 index 0000000..6e2a82c --- /dev/null +++ b/snikket_web/templates/exception.html @@ -0,0 +1,17 @@ +{% extends "unauth.html" %} +{% block head_lead -%} +{% trans %}Internal error{% endtrans %} +{%- endblock %} +{% block content %} +
+ {%- block box %}{% endblock -%} +
+GURU MEDITATION
+{% if traceback %}
+{{- traceback -}}
+{% else %}
+{{- exception_short -}}
+{% endif %}
+error_id={{ error_id }}
+
+{% endblock %} diff --git a/snikket_web/templates/generic_http_error.html b/snikket_web/templates/generic_http_error.html new file mode 100644 index 0000000..6ff5cc2 --- /dev/null +++ b/snikket_web/templates/generic_http_error.html @@ -0,0 +1,13 @@ +{% extends "unauth.html" %} +{% from "library.j2" import standard_button %} +{% block content -%} +
+
{{ name }}
+

{{ description }}.

+
+ {%- call standard_button("back", url_for("index"), class="primary") -%} + {% trans %}Go back to the main page{% endtrans %} + {%- endcall -%} +
+
+{%- endblock %} diff --git a/snikket_web/templates/internal_error.html b/snikket_web/templates/internal_error.html new file mode 100644 index 0000000..1ecdd38 --- /dev/null +++ b/snikket_web/templates/internal_error.html @@ -0,0 +1,6 @@ +{% extends "exception.html" %} +{% block box -%} +
{% trans %}Internal error{% endtrans %}
+

{% trans %}The web portal encountered an internal error.{% endtrans %}

+

{% trans %}Please try again later and/or inform your Snikket instance admin.{% endtrans %}

+{%- endblock %} diff --git a/snikket_web/templates/unauth.html b/snikket_web/templates/unauth.html new file mode 100644 index 0000000..a6767b5 --- /dev/null +++ b/snikket_web/templates/unauth.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% from "library.j2" import box, form_button %} +{% block style %} + +{{ super() }} +{% endblock %} +{% block body %} +
+
{{ config["SNIKKET_DOMAIN"] }}
+ {% block topbar_left %}{% endblock %} +
+ {% block topbar_right %}{% endblock %} +
+
{% block content %}{% endblock %}
+
+
  • {% trans about_url=url_for('main.about') %}A Snikket server{% endtrans %}
+
+{% endblock %} diff --git a/snikket_web/translations/de/LC_MESSAGES/messages.po b/snikket_web/translations/de/LC_MESSAGES/messages.po index 1a720ae..6a771f0 100644 --- a/snikket_web/translations/de/LC_MESSAGES/messages.po +++ b/snikket_web/translations/de/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: SnikketWeb 0.1.0\n" "Report-Msgid-Bugs-To: jonas@zombofant.net\n" -"POT-Creation-Date: 2021-01-21 16:54+0100\n" +"POT-Creation-Date: 2021-01-21 16:55+0100\n" "PO-Revision-Date: 2020-03-07 16:32+0100\n" "Last-Translator: Jonas Schäfer \n" "Language: de\n" @@ -18,71 +18,71 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.0\n" -#: snikket_web/admin.py:54 +#: snikket_web/admin.py:51 msgid "Delete user permanently" msgstr "Benutzer endgültig löschen" -#: snikket_web/admin.py:83 +#: snikket_web/admin.py:78 msgid "Invite to circle" msgstr "In Gemeinschaft einladen" -#: snikket_web/admin.py:89 +#: snikket_web/admin.py:84 msgid "At least one circle must be selected" msgstr "Mindestens eine Gemeinschaft muss ausgewählt sein" -#: snikket_web/admin.py:94 +#: snikket_web/admin.py:89 msgid "Valid for" msgstr "Gültig für" -#: snikket_web/admin.py:96 +#: snikket_web/admin.py:91 msgid "One hour" msgstr "Eine Stunde" -#: snikket_web/admin.py:97 +#: snikket_web/admin.py:92 msgid "Twelve hours" msgstr "Zwölf Stunden" -#: snikket_web/admin.py:98 +#: snikket_web/admin.py:93 msgid "One day" msgstr "Ein Tag" -#: snikket_web/admin.py:99 +#: snikket_web/admin.py:94 msgid "One week" msgstr "Eine Woche" -#: snikket_web/admin.py:100 +#: snikket_web/admin.py:95 msgid "Four weeks" msgstr "Vier Wochen" -#: snikket_web/admin.py:106 +#: snikket_web/admin.py:101 msgid "Allow multiple uses" msgstr "Mehrfach verwendbar" -#: snikket_web/admin.py:110 +#: snikket_web/admin.py:105 msgid "New invitation link" msgstr "Neuer Einladungslink" -#: snikket_web/admin.py:170 +#: snikket_web/admin.py:163 msgid "Revoke" msgstr "Löschen" -#: snikket_web/admin.py:225 snikket_web/admin.py:272 +#: snikket_web/admin.py:214 snikket_web/admin.py:257 msgid "Name" msgstr "Name" -#: snikket_web/admin.py:229 snikket_web/templates/admin_circles.html:42 +#: snikket_web/admin.py:218 snikket_web/templates/admin_circles.html:42 msgid "Create circle" msgstr "Gemeinschaft gründen" -#: snikket_web/admin.py:276 snikket_web/user.py:73 +#: snikket_web/admin.py:261 snikket_web/user.py:68 msgid "Apply" msgstr "Übernehmen" -#: snikket_web/admin.py:280 +#: snikket_web/admin.py:265 msgid "Delete circle permanently" msgstr "Gemeinschaft endgültig löschen" -#: snikket_web/infra.py:39 +#: snikket_web/infra.py:41 msgid "Main" msgstr "Kern" @@ -102,53 +102,53 @@ msgstr "Anmelden" msgid "Invalid user name or password." msgstr "Benutzername oder Passwort falsch." -#: snikket_web/user.py:26 +#: snikket_web/user.py:21 msgid "Current password" msgstr "Aktuelles Passwort" -#: snikket_web/user.py:31 +#: snikket_web/user.py:26 msgid "New password" msgstr "Neues Passwort" -#: snikket_web/user.py:36 +#: snikket_web/user.py:31 msgid "Confirm new password" msgstr "Neues Passwort (Bestätigung)" -#: snikket_web/user.py:40 +#: snikket_web/user.py:35 msgid "The new passwords must match." msgstr "Die neuen Passwörter müssen übereinstimmen." -#: snikket_web/user.py:47 +#: snikket_web/user.py:42 msgid "Sign out" msgstr "Abmelden" -#: snikket_web/user.py:52 +#: snikket_web/user.py:47 msgid "Nobody" msgstr "Niemand" -#: snikket_web/user.py:53 +#: snikket_web/user.py:48 msgid "Friends only" msgstr "Nur Freunde" -#: snikket_web/user.py:54 +#: snikket_web/user.py:49 msgid "Everyone" msgstr "Jeder" #: snikket_web/templates/admin_delete_user.html:12 #: snikket_web/templates/admin_delete_user.html:16 -#: snikket_web/templates/admin_users.html:9 snikket_web/user.py:60 +#: snikket_web/templates/admin_users.html:9 snikket_web/user.py:55 msgid "Display name" msgstr "Anzeigename" -#: snikket_web/user.py:64 +#: snikket_web/user.py:59 msgid "Avatar" msgstr "Bild" -#: snikket_web/user.py:68 +#: snikket_web/user.py:63 msgid "Profile visibility" msgstr "Profilsichtbarkeit" -#: snikket_web/user.py:97 +#: snikket_web/user.py:92 msgid "Incorrect password" msgstr "Ungültiges Passwort" @@ -438,10 +438,22 @@ msgstr "Telefonnummer" msgid "Snikket Web Portal" msgstr "Snikket Webportal" -#: snikket_web/templates/app.html:20 snikket_web/templates/login.html:36 -#, python-format -msgid "A Snikket server" -msgstr "Ein Snikket-Server" +#: snikket_web/templates/backend_error.html:3 +#: snikket_web/templates/exception.html:3 +#: snikket_web/templates/internal_error.html:3 +msgid "Internal error" +msgstr "Interner Fehler" + +#: snikket_web/templates/backend_error.html:4 +msgid "The web portal was not able to communicate with the backend." +msgstr "Das Webportal konnte nicht mit dem Backend kommunizieren." + +#: snikket_web/templates/backend_error.html:5 +#: snikket_web/templates/internal_error.html:5 +msgid "Please try again later and/or inform your Snikket instance admin." +msgstr "" +"Versuche es später noch einmal und/oder informiere den Betreiber deiner " +"Snikket-Instanz." #: snikket_web/templates/copy-snippet.html:106 msgid "Copied to clipboard" @@ -451,6 +463,14 @@ msgstr "Kopiert" msgid "Copy operation failed" msgstr "Kopieren fehlgeschlagen" +#: snikket_web/templates/generic_http_error.html:9 +msgid "Go back to the main page" +msgstr "Zurück zur Hauptseite" + +#: snikket_web/templates/internal_error.html:4 +msgid "The web portal encountered an internal error." +msgstr "Das Webportal hatte einen internen Fehler." + #: snikket_web/templates/library.j2:18 msgid "Copy link" msgstr "Link kopieren" @@ -471,6 +491,11 @@ msgstr "Gib deine Snikket-Adresse und -Passwort ein um dein Konto zu verwalten." msgid "Login failed" msgstr "Anmeldung fehlgeschlagen" +#: snikket_web/templates/login.html:36 snikket_web/templates/unauth.html:16 +#, python-format +msgid "A Snikket server" +msgstr "Ein Snikket-Server" + #: snikket_web/templates/user_home.html:3 msgid "Welcome!" msgstr "Willkommen!" diff --git a/snikket_web/translations/en/LC_MESSAGES/messages.po b/snikket_web/translations/en/LC_MESSAGES/messages.po index b7822b7..ca18fdf 100644 --- a/snikket_web/translations/en/LC_MESSAGES/messages.po +++ b/snikket_web/translations/en/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2021-01-21 16:54+0100\n" +"POT-Creation-Date: 2021-01-21 16:55+0100\n" "PO-Revision-Date: 2020-03-07 16:50+0100\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -18,71 +18,71 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.0\n" -#: snikket_web/admin.py:54 +#: snikket_web/admin.py:51 msgid "Delete user permanently" msgstr "" -#: snikket_web/admin.py:83 +#: snikket_web/admin.py:78 msgid "Invite to circle" msgstr "" -#: snikket_web/admin.py:89 +#: snikket_web/admin.py:84 msgid "At least one circle must be selected" msgstr "" -#: snikket_web/admin.py:94 +#: snikket_web/admin.py:89 msgid "Valid for" msgstr "" -#: snikket_web/admin.py:96 +#: snikket_web/admin.py:91 msgid "One hour" msgstr "" -#: snikket_web/admin.py:97 +#: snikket_web/admin.py:92 msgid "Twelve hours" msgstr "" -#: snikket_web/admin.py:98 +#: snikket_web/admin.py:93 msgid "One day" msgstr "" -#: snikket_web/admin.py:99 +#: snikket_web/admin.py:94 msgid "One week" msgstr "" -#: snikket_web/admin.py:100 +#: snikket_web/admin.py:95 msgid "Four weeks" msgstr "" -#: snikket_web/admin.py:106 +#: snikket_web/admin.py:101 msgid "Allow multiple uses" msgstr "" -#: snikket_web/admin.py:110 +#: snikket_web/admin.py:105 msgid "New invitation link" msgstr "" -#: snikket_web/admin.py:170 +#: snikket_web/admin.py:163 msgid "Revoke" msgstr "" -#: snikket_web/admin.py:225 snikket_web/admin.py:272 +#: snikket_web/admin.py:214 snikket_web/admin.py:257 msgid "Name" msgstr "" -#: snikket_web/admin.py:229 snikket_web/templates/admin_circles.html:42 +#: snikket_web/admin.py:218 snikket_web/templates/admin_circles.html:42 msgid "Create circle" msgstr "" -#: snikket_web/admin.py:276 snikket_web/user.py:73 +#: snikket_web/admin.py:261 snikket_web/user.py:68 msgid "Apply" msgstr "" -#: snikket_web/admin.py:280 +#: snikket_web/admin.py:265 msgid "Delete circle permanently" msgstr "" -#: snikket_web/infra.py:39 +#: snikket_web/infra.py:41 msgid "Main" msgstr "" @@ -103,54 +103,54 @@ msgstr "" msgid "Invalid user name or password." msgstr "" -#: snikket_web/user.py:26 +#: snikket_web/user.py:21 msgid "Current password" msgstr "" -#: snikket_web/user.py:31 +#: snikket_web/user.py:26 msgid "New password" msgstr "" -#: snikket_web/user.py:36 +#: snikket_web/user.py:31 msgid "Confirm new password" msgstr "" -#: snikket_web/user.py:40 +#: snikket_web/user.py:35 msgid "The new passwords must match." msgstr "" -#: snikket_web/user.py:47 +#: snikket_web/user.py:42 #, fuzzy msgid "Sign out" msgstr "" -#: snikket_web/user.py:52 +#: snikket_web/user.py:47 msgid "Nobody" msgstr "" -#: snikket_web/user.py:53 +#: snikket_web/user.py:48 msgid "Friends only" msgstr "" -#: snikket_web/user.py:54 +#: snikket_web/user.py:49 msgid "Everyone" msgstr "" #: snikket_web/templates/admin_delete_user.html:12 #: snikket_web/templates/admin_delete_user.html:16 -#: snikket_web/templates/admin_users.html:9 snikket_web/user.py:60 +#: snikket_web/templates/admin_users.html:9 snikket_web/user.py:55 msgid "Display name" msgstr "" -#: snikket_web/user.py:64 +#: snikket_web/user.py:59 msgid "Avatar" msgstr "" -#: snikket_web/user.py:68 +#: snikket_web/user.py:63 msgid "Profile visibility" msgstr "" -#: snikket_web/user.py:97 +#: snikket_web/user.py:92 msgid "Incorrect password" msgstr "" @@ -425,9 +425,19 @@ msgstr "" msgid "Snikket Web Portal" msgstr "" -#: snikket_web/templates/app.html:20 snikket_web/templates/login.html:36 -#, python-format -msgid "A Snikket server" +#: snikket_web/templates/backend_error.html:3 +#: snikket_web/templates/exception.html:3 +#: snikket_web/templates/internal_error.html:3 +msgid "Internal error" +msgstr "" + +#: snikket_web/templates/backend_error.html:4 +msgid "The web portal was not able to communicate with the backend." +msgstr "" + +#: snikket_web/templates/backend_error.html:5 +#: snikket_web/templates/internal_error.html:5 +msgid "Please try again later and/or inform your Snikket instance admin." msgstr "" #: snikket_web/templates/copy-snippet.html:106 @@ -438,6 +448,14 @@ msgstr "" msgid "Copy operation failed" msgstr "" +#: snikket_web/templates/generic_http_error.html:9 +msgid "Go back to the main page" +msgstr "" + +#: snikket_web/templates/internal_error.html:4 +msgid "The web portal encountered an internal error." +msgstr "" + #: snikket_web/templates/library.j2:18 msgid "Copy link" msgstr "" @@ -458,6 +476,11 @@ msgstr "" msgid "Login failed" msgstr "" +#: snikket_web/templates/login.html:36 snikket_web/templates/unauth.html:16 +#, python-format +msgid "A Snikket server" +msgstr "" + #: snikket_web/templates/user_home.html:3 msgid "Welcome!" msgstr "" diff --git a/snikket_web/user.py b/snikket_web/user.py index 44d3110..2810e18 100644 --- a/snikket_web/user.py +++ b/snikket_web/user.py @@ -16,11 +16,6 @@ from .infra import client bp = Blueprint('user', __name__) -@bp.context_processor -async def proc() -> typing.Mapping[str, typing.Any]: - return {"user_info": await client.get_user_info()} - - class ChangePasswordForm(flask_wtf.FlaskForm): # type:ignore current_password = wtforms.PasswordField( _l("Current password"), @@ -146,6 +141,6 @@ async def logout() -> typing.Union[quart.Response, str]: form = LogoutForm() if form.validate_on_submit(): await client.logout() - return redirect(url_for("main.home")) + return redirect(url_for("main.index")) return await render_template("user_logout.html", form=form)