From 17152f68a4b8594c8c72874936e2ed7028846d10 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Date: Fri, 1 Oct 2021 11:59:03 -0400 Subject: [PATCH] server/posts: add preload directive to post HTML --- server/szurubooru/api/opengraph_api.py | 105 +++++++++++------- server/szurubooru/func/auth.py | 10 ++ server/szurubooru/middleware/authenticator.py | 9 +- server/szurubooru/tests/api/test_opengraph.py | 12 +- .../tests/middleware/test_authenticator.py | 15 --- 5 files changed, 86 insertions(+), 65 deletions(-) diff --git a/server/szurubooru/api/opengraph_api.py b/server/szurubooru/api/opengraph_api.py index 3002dd67..e47177d3 100644 --- a/server/szurubooru/api/opengraph_api.py +++ b/server/szurubooru/api/opengraph_api.py @@ -63,18 +63,17 @@ _apple_touch_startup_images = { def _get_html_template( title: str, - meta_tags: Dict = {}, + header_content: str = "", ) -> Doc: doc = Doc() doc.asis("") with doc.tag("html"): with doc.tag("head"): doc.stag("meta", charset="utf-8") - for name, content in {**_default_meta_tags, **meta_tags}.items(): + for name, content in _default_meta_tags.items(): doc.stag("meta", name=name, content=content) with doc.tag("title"): doc.text(title) - doc.stag("base", href=util.add_url_prefix()) doc.stag( "link", rel="manifest", @@ -115,6 +114,8 @@ def _get_html_template( f"({k}: {v})" for k, v in media.items() ), ) + doc.stag("base", href=util.add_url_prefix()) + doc.asis(header_content) with doc.tag("body"): with doc.tag("div", id="top-navigation-holder"): pass @@ -154,47 +155,73 @@ def get_post_html( ) -> rest.Response: try: post = _get_post(params) - title = f"{config.config['name']} - post {_get_post_id(params)}" + title = f"{config.config['name']} - Post #{_get_post_id(params)}" except posts.InvalidPostIdError: # Return the default template and let the browser JS handle the 404 return _get_html_template() - metadata = { - "og:site_name": config.config["name"], - "og:url": util.add_url_prefix(f"post/{params['post_id']}"), - "og:title": title, - "twitter:title": title, - "og:type": "article", - } - # Note: ctx.user will always be the anonymous user - if auth.has_privilege(ctx.user, "posts:view"): - content_url = util.add_data_prefix(posts.get_post_content_path(post)) - thumbnail_url = util.add_data_prefix( - posts.get_post_thumbnail_path(post) + doc = Doc() + doc.stag("meta", name="og:site_name", content=config.config["name"]) + doc.stag( + "meta", + name="og:url", + content=util.add_url_prefix(f"post/{params['post_id']}"), + ) + doc.stag("meta", name="og:title", content=title), + doc.stag("meta", name="twitter:title", content=title), + doc.stag("meta", name="og:type", content="article"), + + if not auth.anon_has_privilege("posts:view"): + return _get_html_template(title=title, header_content=doc.getvalue()) + + content_url = util.add_data_prefix(posts.get_post_content_path(post)) + thumbnail_url = util.add_data_prefix(posts.get_post_thumbnail_path(post)) + tag_string = " ".join(tag.first_name for tag in post.tags) + + doc.stag("meta", name="og:image:alt", content=tag_string) + doc.stag( + "meta", + name="og:article:published_time", + content=post.creation_time.isoformat(), + ) + if post.last_edit_time: + doc.stag( + "meta", + name="og:article:modified_time", + content=post.last_edit_time.isoformat(), ) - metadata["og:article:published_time"] = post.creation_time.isoformat() - if post.last_edit_time: - metadata[ - "og:article:modified_time" - ] = post.last_edit_time.isoformat() - metadata["og:image:alt"] = " ".join( - tag.first_name for tag in post.tags - ) - if post.type in (model.Post.TYPE_VIDEO): - metadata["twitter:card"] = "player" - metadata["og:video:url"] = content_url - metadata["twitter:player:stream"] = content_url - metadata["og:image:url"] = thumbnail_url - if post.canvas_width and post.canvas_height: - metadata["og:video:width"] = str(post.canvas_width) - metadata["og:video:height"] = str(post.canvas_height) - metadata["twitter:player:width"] = str(post.canvas_width) - metadata["twitter:player:height"] = str(post.canvas_height) - else: - metadata["twitter:card"] = "summary_large_image" - metadata["og:image:url"] = content_url - metadata["twitter:image"] = content_url - return _get_html_template(title=title, meta_tags=metadata) + for tag in post.tags: + doc.stag("meta", name="article:tag", content=tag.first_name) + if post.type in (model.Post.TYPE_VIDEO,): + doc.stag("meta", name="twitter:card", content="player") + doc.stag("meta", name="og:video:url", content=content_url) + doc.stag("meta", name="twitter:player:stream", content=content_url) + doc.stag("meta", name="og:image:url", content=thumbnail_url) + if post.canvas_width and post.canvas_height: + doc.stag( + "meta", name="og:video:width", content=str(post.canvas_width) + ) + doc.stag( + "meta", name="og:video:height", content=str(post.canvas_height) + ) + doc.stag( + "meta", + name="twitter:player:width", + content=str(post.canvas_width), + ) + doc.stag( + "meta", + name="twitter:player:height", + content=str(post.canvas_height), + ) + doc.stag("link", name="preload", href=content_url, **{"as": "video"}) + else: + doc.stag("meta", name="twitter:card", content="summary_large_image") + doc.stag("meta", name="og:image:url", content=content_url) + doc.stag("meta", name="twitter:image", content=content_url) + doc.stag("link", name="preload", href=content_url, **{"as": "image"}) + + return _get_html_template(title=title, header_content=doc.getvalue()) @rest.routes.get("/html/.*", accept="text/html") diff --git a/server/szurubooru/func/auth.py b/server/szurubooru/func/auth.py index d0137756..6251efe2 100644 --- a/server/szurubooru/func/auth.py +++ b/server/szurubooru/func/auth.py @@ -106,6 +106,16 @@ def is_valid_token(user_token: Optional[model.UserToken]) -> bool: return True +def anon_has_privilege(privilege_name: str) -> bool: + all_ranks = list(RANK_MAP.keys()) + assert privilege_name in config.config["privileges"] + minimal_rank = util.flip(RANK_MAP)[ + config.config["privileges"][privilege_name] + ] + good_ranks = all_ranks[all_ranks.index(minimal_rank) :] + return model.User.RANK_ANONYMOUS in good_ranks + + def has_privilege(user: model.User, privilege_name: str) -> bool: assert user all_ranks = list(RANK_MAP.keys()) diff --git a/server/szurubooru/middleware/authenticator.py b/server/szurubooru/middleware/authenticator.py index 56a5e442..e73b235e 100644 --- a/server/szurubooru/middleware/authenticator.py +++ b/server/szurubooru/middleware/authenticator.py @@ -73,11 +73,10 @@ def _get_user(ctx: rest.Context, bump_login: bool) -> Optional[model.User]: def process_request(ctx: rest.Context) -> None: """ Bind the user to request. Update last login time if needed. """ - if ctx.accept == "application/json": - bump_login = ctx.get_param_as_bool("bump-login", default=False) - auth_user = _get_user(ctx, bump_login) - if auth_user: - ctx.user = auth_user + bump_login = ctx.get_param_as_bool("bump-login", default=False) + auth_user = _get_user(ctx, bump_login) + if auth_user: + ctx.user = auth_user @rest.middleware.pre_hook diff --git a/server/szurubooru/tests/api/test_opengraph.py b/server/szurubooru/tests/api/test_opengraph.py index 5dc2ea52..4a50a057 100644 --- a/server/szurubooru/tests/api/test_opengraph.py +++ b/server/szurubooru/tests/api/test_opengraph.py @@ -22,7 +22,7 @@ def test_get_post_html( ): config_injector( { - "name": "test installation", + "name": "testing", "base_url": "/someprefix", "data_url": "data", } @@ -32,20 +32,20 @@ def test_get_post_html( post.canvas_height = 1080 db.session.add(post) db.session.flush() - with patch("szurubooru.func.auth.has_privilege"), patch( + with patch("szurubooru.func.auth.anon_has_privilege"), patch( "szurubooru.func.posts.get_post_content_path" ), patch("szurubooru.func.posts.get_post_thumbnail_path"): - auth.has_privilege.return_value = view_priv + auth.anon_has_privilege.return_value = view_priv posts.get_post_content_path.return_value = "content-url" posts.get_post_thumbnail_path.return_value = "thumbnail-url" ret = api.opengraph_api.get_post_html( context_factory(), {"post_id": 1} ) - assert _make_meta_tag("og:site_name", "test installation") in ret + assert _make_meta_tag("og:site_name", "testing") in ret assert _make_meta_tag("og:url", "/someprefix/post/1") in ret - assert _make_meta_tag("og:title", "test installation - post 1") in ret - assert _make_meta_tag("twitter:title", "test installation - post 1") in ret + assert _make_meta_tag("og:title", "testing - Post #1") in ret + assert _make_meta_tag("twitter:title", "testing - Post #1") in ret assert _make_meta_tag("og:type", "article") in ret assert ( bool( diff --git a/server/szurubooru/tests/middleware/test_authenticator.py b/server/szurubooru/tests/middleware/test_authenticator.py index 325b27c2..93189e66 100644 --- a/server/szurubooru/tests/middleware/test_authenticator.py +++ b/server/szurubooru/tests/middleware/test_authenticator.py @@ -14,21 +14,6 @@ def test_process_request_no_header(context_factory): assert ctx.user.name is None -def test_process_request_non_rest(context_factory, user_factory): - user = user_factory() - ctx = context_factory( - headers={"Authorization": "Basic dGVzdFVzZXI6dGVzdFBhc3N3b3Jk"}, - accept="text/html", - ) - with patch("szurubooru.func.auth.is_valid_password"), patch( - "szurubooru.func.users.get_user_by_name" - ): - users.get_user_by_name.return_value = user - auth.is_valid_password.return_value = True - authenticator.process_request(ctx) - assert ctx.user.name is None - - def test_process_request_bump_login(context_factory, user_factory): user = user_factory() db.session.add(user)