server/posts: add preload directive to post HTML

This commit is contained in:
Shyam Sunder 2021-10-01 11:59:03 -04:00
parent 36a614d954
commit 17152f68a4
5 changed files with 86 additions and 65 deletions

View file

@ -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("<!DOCTYPE html>")
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")

View file

@ -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())

View file

@ -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

View file

@ -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(

View file

@ -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)