server/posts: generate appropriate URIs using config parameters only

This commit is contained in:
Shyam Sunder 2021-09-30 17:21:29 -04:00
parent 299ebfc4c8
commit 36a614d954
22 changed files with 314 additions and 187 deletions

View file

@ -20,7 +20,11 @@ http {
keepalive_timeout 65;
proxy_cache_path /tmp/nginx-cache
levels=1:2 keys_zone=spa_cache:1m max_size=50m inactive=60m use_temp_path=off;
levels=1:2
keys_zone=spa_cache:4m
max_size=50m
inactive=60m
use_temp_path=off;
upstream backend {
server __BACKEND__:6666;
@ -88,6 +92,9 @@ http {
location / {
tcp_nodelay on;
# remove unneeded auth headers to improve caching
proxy_set_header Authorization "";
proxy_cache spa_cache;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;

View file

@ -80,8 +80,8 @@ user@host:szuru$ docker-compose down
If you want to host your website on, (`http://example.com/`) but want
to serve the images on a different domain, (`http://static.example.com/`)
then you can run the backend container with an additional environment
variable `DATA_URL=http://static.example.com/`. Make sure that this
then you can configure the `data_url` variable in your `config.yaml`
(ex: `data_url: http://static.example.com/`). Make sure that this
additional host has access contents to the `/data` volume mounted in the
backend.
@ -89,12 +89,9 @@ user@host:szuru$ docker-compose down
Some users may wish to access the service at a different base URI, such
as `http://example.com/szuru/`, commonly when sharing multiple HTTP
services on one domain using a reverse proxy. This can be configured in
either of the following ways:
- Set the 'domain' value in `config.yaml` to include the prefix, i.e.:
`domain: "http://example.com/szuru" # omit trailing slash`
- Configure the reverse proxy to pass the `X-Forwarded-Prefix` header.
services on one domain using a reverse proxy. For szurubooru to handle
links properly, you must configure the reverse proxy to pass the new
URL prefix (in this case `/szuru`) in the `X-Forwarded-Prefix` header.
Note that this will require a reverse proxy to function. You should set
your reverse proxy to proxy `http(s)://example.com/szuru` to
@ -111,7 +108,7 @@ user@host:szuru$ docker-compose down
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Prefix /szuru;
// optional...
# optional...
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Real-IP $remote_addr;

View file

@ -3,11 +3,28 @@
# shown in the website title and on the front page
name: szurubooru
# full url to the homepage of this szurubooru site, with no trailing slash
domain: # example: http://example.com
# used to salt the users' password hashes and generate filenames for static content
secret: change
# set to the root web address for your instance
# example values:
# - `/` (default) is used when the domain is unknown
# - `https://szuru.example.com/` if you know the specific domain
# and is required if you want email-based password reset
# - `/baseprefix` if you want to host szurubooru on a specific
# prefix and share the domain with other applications
# - `https://www.example.com/szuru` combines both of the above
# also see: "Setting a specific base URI for proxying" in INSTALL.md
base_url: /
# !!should not be changed for the normal docker installation!!
# set to the root web address for static image content
# if it is a relative path with no leading `/`, then this will be
# appended to the base url.
# see: "Using a seperate domain to host static files" in INSTALL.md
# for more info on when to modify
data_url: data/
# Delete thumbnails and source files on post delete
# Original functionality is no, to mitigate the impacts of admins going
# on unchecked post purges.
@ -171,7 +188,6 @@ privileges:
## ONLY SET THESE IF DEPLOYING OUTSIDE OF DOCKER
#debug: 0 # generate server logs?
#show_sql: 0 # show sql in server logs?
#data_url: /data/
#data_dir: /var/www/data
## usage: schema://user:password@host:port/database_name
## example: postgres://szuru:dog@localhost:5432/szuru_test

View file

@ -45,7 +45,7 @@ def get_info(ctx: rest.Context, _params: Dict[str, str] = {}) -> rest.Response:
"defaultUserRank": config.config["default_rank"],
"enableSafety": config.config["enable_safety"],
"contactEmail": config.config["contact_email"],
"canSendMails": bool(config.config["smtp"]["host"]),
"canSendMails": util.can_send_mail(),
"privileges": util.snake_case_to_lower_camel_case_keys(
config.config["privileges"]
),
@ -74,17 +74,17 @@ def generate_manifest(
"name": config.config["name"],
"icons": [
{
"src": f"{ctx.url_prefix}/img/android-chrome-192x192.png",
"src": util.add_url_prefix("/img/android-chrome-192x192.png"),
"type": "image/png",
"sizes": "192x192",
},
{
"src": f"{ctx.url_prefix}/img/android-chrome-512x512.png",
"src": util.add_url_prefix("/img/android-chrome-512x512.png"),
"type": "image/png",
"sizes": "512x512",
},
],
"start_url": f"{ctx.url_prefix}/",
"start_url": util.add_url_prefix(),
"theme_color": "#24aadd",
"background_color": "#ffffff",
"display": "standalone",

View file

@ -1,9 +1,9 @@
from typing import Dict
from typing import Callable, Dict
from yattag import Doc
from szurubooru import config, model, rest
from szurubooru.func import auth, posts
from szurubooru.func import auth, posts, util
_default_meta_tags = {
"viewport": "width=device-width, initial-scale=1, maximum-scale=1",
@ -62,7 +62,8 @@ _apple_touch_startup_images = {
def _get_html_template(
meta_tags: Dict = {}, title: str = config.config["name"], prefix: str = ""
title: str,
meta_tags: Dict = {},
) -> Doc:
doc = Doc()
doc.asis("<!DOCTYPE html>")
@ -73,19 +74,21 @@ def _get_html_template(
doc.stag("meta", name=name, content=content)
with doc.tag("title"):
doc.text(title)
doc.stag("base", href=f"{prefix}/")
doc.stag("base", href=util.add_url_prefix())
doc.stag(
"link", rel="manifest", href=f"{prefix}/api/manifest.json"
"link",
rel="manifest",
href=util.add_url_prefix("/api/manifest.json"),
)
doc.stag(
"link",
href=f"{prefix}/css/app.min.css",
href=util.add_url_prefix("/css/app.min.css"),
rel="stylesheet",
type="text/css",
)
doc.stag(
"link",
href=f"{prefix}/css/vendor.min.css",
href=util.add_url_prefix("/css/vendor.min.css"),
rel="stylesheet",
type="text/css",
)
@ -93,19 +96,21 @@ def _get_html_template(
"link",
rel="shortcut icon",
type="image/png",
href=f"{prefix}/img/favicon.png",
href=util.add_url_prefix("/img/favicon.png"),
)
doc.stag(
"link",
rel="apple-touch-icon",
sizes="180x180",
href=f"{prefix}/img/apple-touch-icon.png",
href=util.add_url_prefix("/img/apple-touch-icon.png"),
)
for res, media in _apple_touch_startup_images.items():
doc.stag(
"link",
rel="apple-touch-startup-image",
href=f"{prefix}/img/apple-touch-startup-image-{res}.png",
href=util.add_url_prefix(
f"/img/apple-touch-startup-image-{res}.png"
),
media=" and ".join(
f"({k}: {v})" for k, v in media.items()
),
@ -116,11 +121,15 @@ def _get_html_template(
with doc.tag("div", id="content-holder"):
pass
with doc.tag(
"script", type="text/javascript", src="js/vendor.min.js"
"script",
type="text/javascript",
src=util.add_url_prefix("js/vendor.min.js"),
):
pass
with doc.tag(
"script", type="text/javascript", src="js/app.min.js"
"script",
type="text/javascript",
src=util.add_url_prefix("js/app.min.js"),
):
pass
return doc.getvalue()
@ -152,13 +161,17 @@ def get_post_html(
metadata = {
"og:site_name": config.config["name"],
"og:url": f"{ctx.url_prefix}/post/{params['post_id']}",
"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)
)
metadata["og:article:published_time"] = post.creation_time.isoformat()
if post.last_edit_time:
metadata[
@ -169,11 +182,9 @@ def get_post_html(
)
if post.type in (model.Post.TYPE_VIDEO):
metadata["twitter:card"] = "player"
metadata["og:video:url"] = posts.get_post_content_url(post)
metadata["twitter:player:stream"] = posts.get_post_content_url(
post
)
metadata["og:image:url"] = posts.get_post_thumbnail_url(post)
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)
@ -181,15 +192,13 @@ def get_post_html(
metadata["twitter:player:height"] = str(post.canvas_height)
else:
metadata["twitter:card"] = "summary_large_image"
metadata["og:image:url"] = posts.get_post_content_url(post)
metadata["twitter:image"] = posts.get_post_content_url(post)
return _get_html_template(
meta_tags=metadata, title=title, prefix=ctx.url_prefix
)
metadata["og:image:url"] = content_url
metadata["twitter:image"] = content_url
return _get_html_template(title=title, meta_tags=metadata)
@rest.routes.get("/html/.*", accept="text/html")
def default_route(
ctx: rest.Context, _params: Dict[str, str] = {}
) -> rest.Response:
return _get_html_template(prefix=ctx.url_prefix)
return _get_html_template(title=config.config["name"])

View file

@ -2,7 +2,7 @@ from hashlib import md5
from typing import Dict
from szurubooru import config, errors, rest
from szurubooru.func import auth, mailer, users, versions
from szurubooru.func import auth, mailer, users, util, versions
MAIL_SUBJECT = "Password reset for {name}"
MAIL_BODY = (
@ -24,8 +24,7 @@ def start_password_reset(
% (user_name)
)
token = auth.generate_authentication_token(user)
url = f"{ctx.url_prefix}/password-reset/{user.name}:{token}"
url = util.add_url_prefix(f"password-reset/{user.name}:{token}")
mailer.send_mail(
config.config["smtp"]["from"],

View file

@ -9,7 +9,8 @@ _search_executor = search.Executor(search.configs.PoolSearchConfig())
def _serialize(ctx: rest.Context, pool: model.Pool) -> rest.Response:
return pools.serialize_pool(
pool, options=serialization.get_serialization_options(ctx)
pool,
options=serialization.get_serialization_options(ctx),
)

View file

@ -35,7 +35,9 @@ def _serialize_post(
ctx: rest.Context, post: Optional[model.Post]
) -> rest.Response:
return posts.serialize_post(
post, ctx.user, options=serialization.get_serialization_options(ctx)
post,
ctx.user,
options=serialization.get_serialization_options(ctx),
)

View file

@ -31,7 +31,6 @@ def _docker_config() -> Dict:
return {
"debug": True,
"show_sql": int(os.getenv("LOG_SQL", 0)),
"data_url": os.getenv("DATA_URL", "data/"),
"data_dir": "/data/",
"database": "postgres://%(user)s:%(pass)s@%(host)s:%(port)d/%(db)s"
% {

View file

@ -85,13 +85,7 @@ def validate_config() -> None:
% (config.config["default_rank"])
)
for key in ["data_url", "data_dir"]:
if not config.config[key]:
raise errors.ConfigError(
"Service is not configured: %r is missing" % key
)
if not os.path.isabs(config.config["data_dir"]):
if not os.path.isabs(config.config["data_dir"] or ""):
raise errors.ConfigError("data_dir must be an absolute path")
if not config.config["database"]:

View file

@ -145,7 +145,8 @@ class PoolSerializer(serialization.BaseSerializer):
def serialize_pool(
pool: model.Pool, options: List[str] = []
pool: model.Pool,
options: List[str] = [],
) -> Optional[rest.Response]:
if not pool:
return None
@ -154,7 +155,8 @@ def serialize_pool(
def serialize_micro_pool(pool: model.Pool) -> Optional[rest.Response]:
return serialize_pool(
pool, options=["id", "names", "category", "description", "postCount"]
pool,
options=["id", "names", "category", "description", "postCount"],
)

View file

@ -44,7 +44,9 @@ class PostAlreadyUploadedError(errors.ValidationError):
super().__init__(
"Post already uploaded (%d)" % other_post.post_id,
{
"otherPostUrl": get_post_content_url(other_post),
"otherPostUrl": util.add_data_prefix(
get_post_content_path(other_post)
),
"otherPostId": other_post.post_id,
},
)
@ -105,25 +107,6 @@ def get_post_security_hash(id: int) -> str:
).hexdigest()[0:16]
def get_post_content_url(post: model.Post) -> str:
assert post
return "%s/posts/%d_%s.%s" % (
config.config["data_url"].rstrip("/"),
post.post_id,
get_post_security_hash(post.post_id),
mime.get_extension(post.mime_type) or "dat",
)
def get_post_thumbnail_url(post: model.Post) -> str:
assert post
return "%s/generated-thumbnails/%d_%s.jpg" % (
config.config["data_url"].rstrip("/"),
post.post_id,
get_post_security_hash(post.post_id),
)
def get_post_content_path(post: model.Post) -> str:
assert post
assert post.post_id
@ -159,7 +142,11 @@ def serialize_note(note: model.PostNote) -> rest.Response:
class PostSerializer(serialization.BaseSerializer):
def __init__(self, post: model.Post, auth_user: model.User) -> None:
def __init__(
self,
post: model.Post,
auth_user: model.User,
) -> None:
self.post = post
self.auth_user = auth_user
@ -241,10 +228,10 @@ class PostSerializer(serialization.BaseSerializer):
return self.post.canvas_height
def serialize_content_url(self) -> Any:
return get_post_content_url(self.post)
return util.add_data_prefix(get_post_content_path(self.post))
def serialize_thumbnail_url(self) -> Any:
return get_post_thumbnail_url(self.post)
return util.add_data_prefix(get_post_thumbnail_path(self.post))
def serialize_flags(self) -> Any:
return self.post.flags
@ -264,7 +251,7 @@ class PostSerializer(serialization.BaseSerializer):
{
post["id"]: post
for post in [
serialize_micro_post(rel, self.auth_user)
serialize_micro_post(rel, self.auth_user, self.url_prefix)
for rel in self.post.relations
]
}.values(),
@ -346,7 +333,9 @@ class PostSerializer(serialization.BaseSerializer):
def serialize_post(
post: Optional[model.Post], auth_user: model.User, options: List[str] = []
post: Optional[model.Post],
auth_user: model.User,
options: List[str] = [],
) -> Optional[rest.Response]:
if not post:
return None
@ -354,7 +343,8 @@ def serialize_post(
def serialize_micro_post(
post: model.Post, auth_user: model.User
post: model.Post,
auth_user: model.User,
) -> Optional[rest.Response]:
return serialize_post(
post, auth_user=auth_user, options=["id", "thumbnailUrl"]

View file

@ -5,8 +5,9 @@ import tempfile
from contextlib import contextmanager
from datetime import datetime, timedelta
from typing import Any, Dict, Generator, List, Optional, Tuple, TypeVar, Union
from urllib.parse import urlparse, urlunparse
from szurubooru import errors
from szurubooru import config, errors
T = TypeVar("T")
@ -176,3 +177,34 @@ def get_column_size(column: Any) -> Optional[int]:
def chunks(source_list: List[Any], part_size: int) -> Generator:
for i in range(0, len(source_list), part_size):
yield source_list[i : i + part_size]
def _get_url_prefix_parts() -> str:
parsed_base_url = list(urlparse(config.config["base_url"]))
if not all(parsed_base_url[0:2]):
parsed_base_url[0:2] = ["", ""]
parsed_base_url[2] = parsed_base_url[2].rstrip("/")
return parsed_base_url[0:3] + ["", "", ""]
def _get_data_prefix_parts() -> str:
parsed_base_url = _get_url_prefix_parts()
parsed_data_url = list(urlparse(config.config["data_url"]))
if not all(parsed_data_url[0:2]):
parsed_data_url[0:2] = parsed_base_url[0:2]
if not parsed_data_url[2].startswith("/"):
parsed_data_url[2] = parsed_base_url[2] + "/" + parsed_data_url[2]
parsed_data_url[2] = parsed_data_url[2].rstrip("/")
return parsed_data_url[0:3] + ["", "", ""]
def add_url_prefix(url: str = "") -> str:
return urlunparse(_get_url_prefix_parts()) + "/" + url.lstrip("/")
def add_data_prefix(url: str = "") -> str:
return urlunparse(_get_data_prefix_parts()) + "/" + url.lstrip("/")
def can_send_mail() -> bool:
return bool(config.config["smtp"]["host"] and _get_url_prefix_parts()[1])

View file

@ -41,11 +41,6 @@ def _create_context(env: Dict[str, Any]) -> context.Context:
path = path.encode("latin-1").decode("utf-8") # PEP-3333
headers = _get_headers(env)
if config.config["domain"]:
url_prefix = config.config["domain"].rstrip("/")
else:
url_prefix = headers.get("X-Forwarded-Prefix", "")
files = {}
params = dict(urllib.parse.parse_qsl(env.get("QUERY_STRING", "")))
@ -80,7 +75,6 @@ def _create_context(env: Dict[str, Any]) -> context.Context:
method=method,
url=path,
headers=headers,
url_prefix=url_prefix,
params=params,
files=files,
)

View file

@ -16,7 +16,6 @@ class Context:
url: str,
headers: Dict[str, str] = None,
accept: str = None,
url_prefix: str = None,
params: Request = None,
files: Dict[str, bytes] = None,
) -> None:
@ -24,7 +23,6 @@ class Context:
self.method = method
self.url = url
self.accept = accept
self.url_prefix = url_prefix
self._headers = headers or {}
self._params = params or {}
self._files = files or {}

View file

@ -18,6 +18,7 @@ def test_info_api(
config_injector(
{
"name": "test installation",
"base_url": "https://www.example.com",
"contact_email": "test@example.com",
"enable_safety": True,
"data_dir": str(directory),
@ -97,9 +98,13 @@ def test_info_api(
def test_manifest(config_injector, context_factory):
config_injector({"name": "test installation"})
config_injector(
{
"name": "test installation",
"base_url": "/someprefix",
}
)
ctx = context_factory()
ctx.url_prefix = "/someprefix"
expected_manifest = {
"name": "test installation",
"icons": [

View file

@ -23,23 +23,24 @@ def test_get_post_html(
config_injector(
{
"name": "test installation",
"data_url": "data/",
"base_url": "/someprefix",
"data_url": "data",
}
)
ctx = context_factory()
ctx.url_prefix = "/someprefix"
post = post_factory(id=1, type=post_type)
post.canvas_width = 1920
post.canvas_height = 1080
db.session.add(post)
db.session.flush()
with patch("szurubooru.func.auth.has_privilege"), patch(
"szurubooru.func.posts.get_post_content_url"
), patch("szurubooru.func.posts.get_post_thumbnail_url"):
"szurubooru.func.posts.get_post_content_path"
), patch("szurubooru.func.posts.get_post_thumbnail_path"):
auth.has_privilege.return_value = view_priv
posts.get_post_content_url.return_value = "/content-url"
posts.get_post_thumbnail_url.return_value = "/thumbnail-url"
ret = api.opengraph_api.get_post_html(ctx, {"post_id": 1})
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:url", "/someprefix/post/1") in ret
@ -59,16 +60,27 @@ def test_get_post_html(
)
assert (
bool(
_make_meta_tag("twitter:player:stream", "/content-url") in ret
_make_meta_tag(
"twitter:player:stream", "/someprefix/data/content-url"
)
in ret
)
== view_priv
)
assert (
bool(_make_meta_tag("og:video:url", "/content-url") in ret)
bool(
_make_meta_tag("og:video:url", "/someprefix/data/content-url")
in ret
)
== view_priv
)
assert (
bool(_make_meta_tag("og:image:url", "/thumbnail-url") in ret)
bool(
_make_meta_tag(
"og:image:url", "/someprefix/data/thumbnail-url"
)
in ret
)
== view_priv
)
assert (
@ -83,6 +95,9 @@ def test_get_post_html(
== view_priv
)
assert (
bool(_make_meta_tag("twitter:image", "/content-url") in ret)
bool(
_make_meta_tag("twitter:image", "/someprefix/data/content-url")
in ret
)
== view_priv
)

View file

@ -11,7 +11,7 @@ def inject_config(config_injector):
config_injector(
{
"secret": "x",
"domain": "http://example.com",
"base_url": "http://example.com",
"name": "Test instance",
"smtp": {
"from": "noreply@example.com",
@ -27,13 +27,11 @@ def test_reset_sending_email(context_factory, user_factory):
)
)
db.session.flush()
ctx = context_factory()
ctx.url_prefix = "http://example.com"
for initiating_user in ["u1", "user@example.com"]:
with patch("szurubooru.func.mailer.send_mail"):
assert (
api.password_reset_api.start_password_reset(
ctx, {"user_name": initiating_user}
context_factory(), {"user_name": initiating_user}
)
== {}
)

View file

@ -41,19 +41,17 @@ def test_simple_updating(user_factory, pool_factory, context_factory):
):
posts.get_posts_by_ids.return_value = ([], [])
pools.serialize_pool.return_value = "serialized pool"
result = api.pool_api.update_pool(
context_factory(
params={
"version": 1,
"names": ["pool3"],
"category": "series",
"description": "desc",
"posts": [1, 2],
},
user=auth_user,
),
{"pool_id": 1},
ctx = context_factory(
params={
"version": 1,
"names": ["pool3"],
"category": "series",
"description": "desc",
"posts": [1, 2],
},
user=auth_user,
)
result = api.pool_api.update_pool(ctx, {"pool_id": 1})
assert result == "serialized pool"
pools.create_pool.assert_not_called()
pools.update_pool_names.assert_called_once_with(pool, ["pool3"])

View file

@ -45,19 +45,18 @@ def test_creating_minimal_posts(context_factory, post_factory, user_factory):
posts.create_post.return_value = (post, [])
posts.serialize_post.return_value = "serialized post"
result = api.post_api.create_post(
context_factory(
params={
"safety": "safe",
"tags": ["tag1", "tag2"],
},
files={
"content": "post-content",
"thumbnail": "post-thumbnail",
},
user=auth_user,
)
ctx = context_factory(
params={
"safety": "safe",
"tags": ["tag1", "tag2"],
},
files={
"content": "post-content",
"thumbnail": "post-thumbnail",
},
user=auth_user,
)
result = api.post_api.create_post(ctx)
assert result == "serialized post"
posts.create_post.assert_called_once_with(
@ -102,22 +101,21 @@ def test_creating_full_posts(context_factory, post_factory, user_factory):
posts.create_post.return_value = (post, [])
posts.serialize_post.return_value = "serialized post"
result = api.post_api.create_post(
context_factory(
params={
"safety": "safe",
"tags": ["tag1", "tag2"],
"relations": [1, 2],
"source": "source",
"notes": ["note1", "note2"],
"flags": ["flag1", "flag2"],
},
files={
"content": "post-content",
},
user=auth_user,
)
ctx = context_factory(
params={
"safety": "safe",
"tags": ["tag1", "tag2"],
"relations": [1, 2],
"source": "source",
"notes": ["note1", "note2"],
"flags": ["flag1", "flag2"],
},
files={
"content": "post-content",
},
user=auth_user,
)
result = api.post_api.create_post(ctx)
assert result == "serialized post"
posts.create_post.assert_called_once_with(
@ -333,7 +331,8 @@ def test_errors_not_spending_ids(
config_injector(
{
"data_dir": str(tmpdir.mkdir("data")),
"data_url": "example.com",
"base_url": "https://example.com/",
"data_url": "https://example.com/data",
"thumbnails": {
"post_width": 300,
"post_height": 300,

View file

@ -17,34 +17,6 @@ from szurubooru.func import (
)
@pytest.mark.parametrize(
"input_mime_type,expected_url",
[
("image/jpeg", "http://example.com/posts/1_244c8840887984c4.jpg"),
("image/gif", "http://example.com/posts/1_244c8840887984c4.gif"),
("totally/unknown", "http://example.com/posts/1_244c8840887984c4.dat"),
],
)
def test_get_post_url(input_mime_type, expected_url, config_injector):
config_injector({"data_url": "http://example.com/", "secret": "test"})
post = model.Post()
post.post_id = 1
post.mime_type = input_mime_type
assert posts.get_post_content_url(post) == expected_url
@pytest.mark.parametrize("input_mime_type", ["image/jpeg", "image/gif"])
def test_get_post_thumbnail_url(input_mime_type, config_injector):
config_injector({"data_url": "http://example.com/", "secret": "test"})
post = model.Post()
post.post_id = 1
post.mime_type = input_mime_type
assert (
posts.get_post_thumbnail_url(post)
== "http://example.com/generated-thumbnails/1_244c8840887984c4.jpg"
)
@pytest.mark.parametrize(
"input_mime_type,expected_path",
[
@ -53,7 +25,10 @@ def test_get_post_thumbnail_url(input_mime_type, config_injector):
("totally/unknown", "posts/1_244c8840887984c4.dat"),
],
)
def test_get_post_content_path(input_mime_type, expected_path):
def test_get_post_content_path(
input_mime_type, expected_path, config_injector
):
config_injector({"secret": "test"})
post = model.Post()
post.post_id = 1
post.mime_type = input_mime_type
@ -61,7 +36,8 @@ def test_get_post_content_path(input_mime_type, expected_path):
@pytest.mark.parametrize("input_mime_type", ["image/jpeg", "image/gif"])
def test_get_post_thumbnail_path(input_mime_type):
def test_get_post_thumbnail_path(input_mime_type, config_injector):
config_injector({"secret": "test"})
post = model.Post()
post.post_id = 1
post.mime_type = input_mime_type
@ -72,7 +48,8 @@ def test_get_post_thumbnail_path(input_mime_type):
@pytest.mark.parametrize("input_mime_type", ["image/jpeg", "image/gif"])
def test_get_post_thumbnail_backup_path(input_mime_type):
def test_get_post_thumbnail_backup_path(input_mime_type, config_injector):
config_injector({"secret": "test"})
post = model.Post()
post.post_id = 1
post.mime_type = input_mime_type
@ -105,7 +82,13 @@ def test_serialize_post(
pool_category_factory,
config_injector,
):
config_injector({"data_url": "http://example.com/", "secret": "test"})
config_injector(
{
"secret": "test",
"base_url": "http://example.com/",
"data_url": "http://example.com/",
}
)
with patch("szurubooru.func.comments.serialize_comment"), patch(
"szurubooru.func.users.serialize_micro_user"
), patch("szurubooru.func.posts.files.has"):
@ -277,17 +260,15 @@ def test_serialize_post(
def test_serialize_micro_post(post_factory, user_factory):
with patch("szurubooru.func.posts.get_post_thumbnail_url"):
posts.get_post_thumbnail_url.return_value = (
"https://example.com/thumb.png"
)
with patch("szurubooru.func.posts.get_post_thumbnail_path"):
posts.get_post_thumbnail_path.return_value = "thumb.png"
auth_user = user_factory()
post = post_factory()
db.session.add(post)
db.session.flush()
assert posts.serialize_micro_post(post, auth_user) == {
"id": post.post_id,
"thumbnailUrl": "https://example.com/thumb.png",
"thumbnailUrl": "http://example.com/thumb.png",
}
@ -519,7 +500,8 @@ def test_update_post_content_to_existing_content(
config_injector(
{
"data_dir": str(tmpdir.mkdir("data")),
"data_url": "example.com",
"base_url": "https://example.com/",
"data_url": "https://example.com/data",
"thumbnails": {
"post_width": 300,
"post_height": 300,

View file

@ -45,3 +45,93 @@ def test_parsing_date_time(fake_datetime, input, output):
)
def test_icase_unique(input, output):
assert util.icase_unique(input) == output
def test_url_generation(config_injector):
config_injector(
{
"base_url": "https://www.example.com/",
"data_url": "data/",
}
)
assert util.add_url_prefix() == "https://www.example.com/"
assert util.add_url_prefix("/post/1") == "https://www.example.com/post/1"
assert util.add_url_prefix("post/1") == "https://www.example.com/post/1"
assert util.add_data_prefix() == "https://www.example.com/data/"
assert (
util.add_data_prefix("posts/1.jpg")
== "https://www.example.com/data/posts/1.jpg"
)
assert (
util.add_data_prefix("/posts/1.jpg")
== "https://www.example.com/data/posts/1.jpg"
)
config_injector(
{
"base_url": "https://www.example.com/szuru/",
"data_url": "data/",
}
)
assert util.add_url_prefix() == "https://www.example.com/szuru/"
assert (
util.add_url_prefix("/post/1")
== "https://www.example.com/szuru/post/1"
)
assert (
util.add_url_prefix("post/1") == "https://www.example.com/szuru/post/1"
)
assert util.add_data_prefix() == "https://www.example.com/szuru/data/"
assert (
util.add_data_prefix("posts/1.jpg")
== "https://www.example.com/szuru/data/posts/1.jpg"
)
assert (
util.add_data_prefix("/posts/1.jpg")
== "https://www.example.com/szuru/data/posts/1.jpg"
)
config_injector(
{
"base_url": "https://www.example.com/szuru/",
"data_url": "/data/",
}
)
assert util.add_url_prefix() == "https://www.example.com/szuru/"
assert (
util.add_url_prefix("/post/1")
== "https://www.example.com/szuru/post/1"
)
assert (
util.add_url_prefix("post/1") == "https://www.example.com/szuru/post/1"
)
assert util.add_data_prefix() == "https://www.example.com/data/"
assert (
util.add_data_prefix("posts/1.jpg")
== "https://www.example.com/data/posts/1.jpg"
)
assert (
util.add_data_prefix("/posts/1.jpg")
== "https://www.example.com/data/posts/1.jpg"
)
config_injector(
{
"base_url": "https://www.example.com/szuru",
"data_url": "https://static.example.com/",
}
)
assert util.add_url_prefix() == "https://www.example.com/szuru/"
assert (
util.add_url_prefix("/post/1")
== "https://www.example.com/szuru/post/1"
)
assert (
util.add_url_prefix("post/1") == "https://www.example.com/szuru/post/1"
)
assert util.add_data_prefix() == "https://static.example.com/"
assert (
util.add_data_prefix("posts/1.jpg")
== "https://static.example.com/posts/1.jpg"
)
assert (
util.add_data_prefix("/posts/1.jpg")
== "https://static.example.com/posts/1.jpg"
)