From 36a614d9549e88cf94bf0bc54da663782659a400 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Date: Thu, 30 Sep 2021 17:21:29 -0400 Subject: [PATCH] server/posts: generate appropriate URIs using config parameters only --- client/nginx.conf.docker | 9 +- doc/INSTALL.md | 15 ++-- server/config.yaml.dist | 22 ++++- server/szurubooru/api/info_api.py | 8 +- server/szurubooru/api/opengraph_api.py | 57 +++++++----- server/szurubooru/api/password_reset_api.py | 5 +- server/szurubooru/api/pool_api.py | 3 +- server/szurubooru/api/post_api.py | 4 +- server/szurubooru/config.py | 1 - server/szurubooru/facade.py | 8 +- server/szurubooru/func/pools.py | 6 +- server/szurubooru/func/posts.py | 42 ++++----- server/szurubooru/func/util.py | 34 ++++++- server/szurubooru/rest/app.py | 6 -- server/szurubooru/rest/context.py | 2 - server/szurubooru/tests/api/test_info.py | 9 +- server/szurubooru/tests/api/test_opengraph.py | 39 +++++--- .../tests/api/test_password_reset.py | 6 +- .../tests/api/test_pool_updating.py | 22 +++-- .../tests/api/test_post_creating.py | 55 ++++++------ server/szurubooru/tests/func/test_posts.py | 58 +++++------- server/szurubooru/tests/func/test_util.py | 90 +++++++++++++++++++ 22 files changed, 314 insertions(+), 187 deletions(-) diff --git a/client/nginx.conf.docker b/client/nginx.conf.docker index fef96bd1..1ce531d7 100644 --- a/client/nginx.conf.docker +++ b/client/nginx.conf.docker @@ -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; diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 90e24c16..fda1e9be 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -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; diff --git a/server/config.yaml.dist b/server/config.yaml.dist index bc4e3630..6936423f 100644 --- a/server/config.yaml.dist +++ b/server/config.yaml.dist @@ -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 diff --git a/server/szurubooru/api/info_api.py b/server/szurubooru/api/info_api.py index d237631f..e73a6e5c 100644 --- a/server/szurubooru/api/info_api.py +++ b/server/szurubooru/api/info_api.py @@ -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", diff --git a/server/szurubooru/api/opengraph_api.py b/server/szurubooru/api/opengraph_api.py index f71bc853..3002dd67 100644 --- a/server/szurubooru/api/opengraph_api.py +++ b/server/szurubooru/api/opengraph_api.py @@ -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("") @@ -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"]) diff --git a/server/szurubooru/api/password_reset_api.py b/server/szurubooru/api/password_reset_api.py index c0e0d60f..40b50e96 100644 --- a/server/szurubooru/api/password_reset_api.py +++ b/server/szurubooru/api/password_reset_api.py @@ -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"], diff --git a/server/szurubooru/api/pool_api.py b/server/szurubooru/api/pool_api.py index a2fb716b..52168325 100644 --- a/server/szurubooru/api/pool_api.py +++ b/server/szurubooru/api/pool_api.py @@ -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), ) diff --git a/server/szurubooru/api/post_api.py b/server/szurubooru/api/post_api.py index daba7f7e..6d5e43c3 100644 --- a/server/szurubooru/api/post_api.py +++ b/server/szurubooru/api/post_api.py @@ -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), ) diff --git a/server/szurubooru/config.py b/server/szurubooru/config.py index 1515a54f..37d37a89 100644 --- a/server/szurubooru/config.py +++ b/server/szurubooru/config.py @@ -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" % { diff --git a/server/szurubooru/facade.py b/server/szurubooru/facade.py index a7e48449..9d0a446d 100644 --- a/server/szurubooru/facade.py +++ b/server/szurubooru/facade.py @@ -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"]: diff --git a/server/szurubooru/func/pools.py b/server/szurubooru/func/pools.py index c3ea9f0f..4accc423 100644 --- a/server/szurubooru/func/pools.py +++ b/server/szurubooru/func/pools.py @@ -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"], ) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index be2259cf..b0645ec8 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -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"] diff --git a/server/szurubooru/func/util.py b/server/szurubooru/func/util.py index f8391365..112ed390 100644 --- a/server/szurubooru/func/util.py +++ b/server/szurubooru/func/util.py @@ -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]) diff --git a/server/szurubooru/rest/app.py b/server/szurubooru/rest/app.py index 5f92f0a1..54f16f37 100644 --- a/server/szurubooru/rest/app.py +++ b/server/szurubooru/rest/app.py @@ -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, ) diff --git a/server/szurubooru/rest/context.py b/server/szurubooru/rest/context.py index 9f00d0f0..2fc4b10e 100644 --- a/server/szurubooru/rest/context.py +++ b/server/szurubooru/rest/context.py @@ -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 {} diff --git a/server/szurubooru/tests/api/test_info.py b/server/szurubooru/tests/api/test_info.py index 3df3958f..113f198c 100644 --- a/server/szurubooru/tests/api/test_info.py +++ b/server/szurubooru/tests/api/test_info.py @@ -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": [ diff --git a/server/szurubooru/tests/api/test_opengraph.py b/server/szurubooru/tests/api/test_opengraph.py index 4a63ecbc..5dc2ea52 100644 --- a/server/szurubooru/tests/api/test_opengraph.py +++ b/server/szurubooru/tests/api/test_opengraph.py @@ -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 ) diff --git a/server/szurubooru/tests/api/test_password_reset.py b/server/szurubooru/tests/api/test_password_reset.py index d6195880..effa2e46 100644 --- a/server/szurubooru/tests/api/test_password_reset.py +++ b/server/szurubooru/tests/api/test_password_reset.py @@ -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} ) == {} ) diff --git a/server/szurubooru/tests/api/test_pool_updating.py b/server/szurubooru/tests/api/test_pool_updating.py index 507289f6..745c3a0d 100644 --- a/server/szurubooru/tests/api/test_pool_updating.py +++ b/server/szurubooru/tests/api/test_pool_updating.py @@ -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"]) diff --git a/server/szurubooru/tests/api/test_post_creating.py b/server/szurubooru/tests/api/test_post_creating.py index a1ad4de7..d6f7ab8d 100644 --- a/server/szurubooru/tests/api/test_post_creating.py +++ b/server/szurubooru/tests/api/test_post_creating.py @@ -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, diff --git a/server/szurubooru/tests/func/test_posts.py b/server/szurubooru/tests/func/test_posts.py index fa1b3bb6..acc108bc 100644 --- a/server/szurubooru/tests/func/test_posts.py +++ b/server/szurubooru/tests/func/test_posts.py @@ -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, diff --git a/server/szurubooru/tests/func/test_util.py b/server/szurubooru/tests/func/test_util.py index f42ba29e..359fab35 100644 --- a/server/szurubooru/tests/func/test_util.py +++ b/server/szurubooru/tests/func/test_util.py @@ -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" + )