Merge pull request #3350 from Minnozz/custom-port

Correctly handle serving BookWyrm on custom port
This commit is contained in:
Mouse Reeve 2024-04-24 15:27:01 -07:00 committed by GitHub
commit ad830dd885
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 150 additions and 174 deletions

View file

@ -16,6 +16,11 @@ DEFAULT_LANGUAGE="English"
## Leave unset to allow all hosts ## Leave unset to allow all hosts
# ALLOWED_HOSTS="localhost,127.0.0.1,[::1]" # ALLOWED_HOSTS="localhost,127.0.0.1,[::1]"
# Specify when the site is served from a port that is not the default
# for the protocol (80 for HTTP or 443 for HTTPS).
# Probably only necessary in development.
# PORT=1333
MEDIA_ROOT=images/ MEDIA_ROOT=images/
# Database configuration # Database configuration
@ -139,9 +144,9 @@ HTTP_X_FORWARDED_PROTO=false
TWO_FACTOR_LOGIN_VALIDITY_WINDOW=2 TWO_FACTOR_LOGIN_VALIDITY_WINDOW=2
TWO_FACTOR_LOGIN_MAX_SECONDS=60 TWO_FACTOR_LOGIN_MAX_SECONDS=60
# Additional hosts to allow in the Content-Security-Policy, "self" (should be DOMAIN) # Additional hosts to allow in the Content-Security-Policy, "self" (should be
# and AWS_S3_CUSTOM_DOMAIN (if used) are added by default. # DOMAIN with optionally ":" + PORT) and AWS_S3_CUSTOM_DOMAIN (if used) are
# Value should be a comma-separated list of host names. # added by default. Value should be a comma-separated list of host names.
CSP_ADDITIONAL_HOSTS= CSP_ADDITIONAL_HOSTS=
# Time before being logged out (in seconds) # Time before being logged out (in seconds)

View file

@ -118,9 +118,11 @@ def get_connectors() -> Iterator[abstract_connector.AbstractConnector]:
def get_or_create_connector(remote_id: str) -> abstract_connector.AbstractConnector: def get_or_create_connector(remote_id: str) -> abstract_connector.AbstractConnector:
"""get the connector related to the object's server""" """get the connector related to the object's server"""
url = urlparse(remote_id) url = urlparse(remote_id)
identifier = url.netloc identifier = url.hostname
if not identifier: if not identifier:
raise ValueError("Invalid remote id") raise ValueError(f"Invalid remote id: {remote_id}")
base_url = f"{url.scheme}://{url.netloc}"
try: try:
connector_info = models.Connector.objects.get(identifier=identifier) connector_info = models.Connector.objects.get(identifier=identifier)
@ -128,10 +130,10 @@ def get_or_create_connector(remote_id: str) -> abstract_connector.AbstractConnec
connector_info = models.Connector.objects.create( connector_info = models.Connector.objects.create(
identifier=identifier, identifier=identifier,
connector_file="bookwyrm_connector", connector_file="bookwyrm_connector",
base_url=f"https://{identifier}", base_url=base_url,
books_url=f"https://{identifier}/book", books_url=f"{base_url}/book",
covers_url=f"https://{identifier}/images/covers", covers_url=f"{base_url}/images/covers",
search_url=f"https://{identifier}/search?q=", search_url=f"{base_url}/search?q=",
priority=2, priority=2,
) )
@ -188,8 +190,11 @@ def raise_not_valid_url(url: str) -> None:
if not parsed.scheme in ["http", "https"]: if not parsed.scheme in ["http", "https"]:
raise ConnectorException("Invalid scheme: ", url) raise ConnectorException("Invalid scheme: ", url)
if not parsed.hostname:
raise ConnectorException("Hostname missing: ", url)
try: try:
ipaddress.ip_address(parsed.netloc) ipaddress.ip_address(parsed.hostname)
raise ConnectorException("Provided url is an IP address: ", url) raise ConnectorException("Provided url is an IP address: ", url)
except ValueError: except ValueError:
# it's not an IP address, which is good # it's not an IP address, which is good

View file

@ -4,7 +4,7 @@ from django.template.loader import get_template
from bookwyrm import models, settings from bookwyrm import models, settings
from bookwyrm.tasks import app, EMAIL from bookwyrm.tasks import app, EMAIL
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN, BASE_URL
def email_data(): def email_data():
@ -14,6 +14,7 @@ def email_data():
"site_name": site.name, "site_name": site.name,
"logo": site.logo_small_url, "logo": site.logo_small_url,
"domain": DOMAIN, "domain": DOMAIN,
"base_url": BASE_URL,
"user": None, "user": None,
} }

View file

@ -26,7 +26,7 @@ class FileLinkForm(CustomForm):
url = cleaned_data.get("url") url = cleaned_data.get("url")
filetype = cleaned_data.get("filetype") filetype = cleaned_data.get("filetype")
book = cleaned_data.get("book") book = cleaned_data.get("book")
domain = urlparse(url).netloc domain = urlparse(url).hostname
if models.LinkDomain.objects.filter(domain=domain).exists(): if models.LinkDomain.objects.filter(domain=domain).exists():
status = models.LinkDomain.objects.get(domain=domain).status status = models.LinkDomain.objects.get(domain=domain).status
if status == "blocked": if status == "blocked":

View file

@ -8,7 +8,7 @@ from django.contrib.postgres.indexes import GinIndex
import pgtrigger import pgtrigger
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
from bookwyrm.utils.db import format_trigger from bookwyrm.utils.db import format_trigger
from .book import BookDataModel, MergedAuthor from .book import BookDataModel, MergedAuthor
@ -70,7 +70,7 @@ class Author(BookDataModel):
def get_remote_id(self): def get_remote_id(self):
"""editions and works both use "book" instead of model_name""" """editions and works both use "book" instead of model_name"""
return f"https://{DOMAIN}/author/{self.id}" return f"{BASE_URL}/author/{self.id}"
class Meta: class Meta:
"""sets up indexes and triggers""" """sets up indexes and triggers"""

View file

@ -10,7 +10,7 @@ from django.http import Http404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.text import slugify from django.utils.text import slugify
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
from .fields import RemoteIdField from .fields import RemoteIdField
@ -38,7 +38,7 @@ class BookWyrmModel(models.Model):
def get_remote_id(self): def get_remote_id(self):
"""generate the url that resolves to the local object, without a slug""" """generate the url that resolves to the local object, without a slug"""
base_path = f"https://{DOMAIN}" base_path = BASE_URL
if hasattr(self, "user"): if hasattr(self, "user"):
base_path = f"{base_path}{self.user.local_path}" base_path = f"{base_path}{self.user.local_path}"
@ -53,7 +53,7 @@ class BookWyrmModel(models.Model):
@property @property
def local_path(self): def local_path(self):
"""how to link to this object in the local app, with a slug""" """how to link to this object in the local app, with a slug"""
local = self.get_remote_id().replace(f"https://{DOMAIN}", "") local = self.get_remote_id().replace(BASE_URL, "")
name = None name = None
if hasattr(self, "name_field"): if hasattr(self, "name_field"):

View file

@ -21,7 +21,7 @@ from bookwyrm import activitypub
from bookwyrm.isbn.isbn import hyphenator_singleton as hyphenator from bookwyrm.isbn.isbn import hyphenator_singleton as hyphenator
from bookwyrm.preview_images import generate_edition_preview_image_task from bookwyrm.preview_images import generate_edition_preview_image_task
from bookwyrm.settings import ( from bookwyrm.settings import (
DOMAIN, BASE_URL,
DEFAULT_LANGUAGE, DEFAULT_LANGUAGE,
LANGUAGE_ARTICLES, LANGUAGE_ARTICLES,
ENABLE_PREVIEW_IMAGES, ENABLE_PREVIEW_IMAGES,
@ -327,7 +327,7 @@ class Book(BookDataModel):
def get_remote_id(self): def get_remote_id(self):
"""editions and works both use "book" instead of model_name""" """editions and works both use "book" instead of model_name"""
return f"https://{DOMAIN}/book/{self.id}" return f"{BASE_URL}/book/{self.id}"
def guess_sort_title(self): def guess_sort_title(self):
"""Get a best-guess sort title for the current book""" """Get a best-guess sort title for the current book"""

View file

@ -11,7 +11,7 @@ ConnectorFiles = models.TextChoices("ConnectorFiles", CONNECTORS)
class Connector(BookWyrmModel): class Connector(BookWyrmModel):
"""book data source connectors""" """book data source connectors"""
identifier = models.CharField(max_length=255, unique=True) identifier = models.CharField(max_length=255, unique=True) # domain
priority = models.IntegerField(default=2) priority = models.IntegerField(default=2)
name = models.CharField(max_length=255, null=True, blank=True) name = models.CharField(max_length=255, null=True, blank=True)
connector_file = models.CharField(max_length=255, choices=ConnectorFiles.choices) connector_file = models.CharField(max_length=255, choices=ConnectorFiles.choices)

View file

@ -16,7 +16,7 @@ FederationStatus = [
class FederatedServer(BookWyrmModel): class FederatedServer(BookWyrmModel):
"""store which servers we federate with""" """store which servers we federate with"""
server_name = models.CharField(max_length=255, unique=True) server_name = models.CharField(max_length=255, unique=True) # domain
status = models.CharField( status = models.CharField(
max_length=255, default="federated", choices=FederationStatus max_length=255, default="federated", choices=FederationStatus
) )
@ -64,5 +64,4 @@ class FederatedServer(BookWyrmModel):
def is_blocked(cls, url: str) -> bool: def is_blocked(cls, url: str) -> bool:
"""look up if a domain is blocked""" """look up if a domain is blocked"""
url = urlparse(url) url = urlparse(url)
domain = url.netloc return cls.objects.filter(server_name=url.hostname, status="blocked").exists()
return cls.objects.filter(server_name=domain, status="blocked").exists()

View file

@ -1,7 +1,7 @@
""" do book related things with other users """ """ do book related things with other users """
from django.db import models, IntegrityError, transaction from django.db import models, IntegrityError, transaction
from django.db.models import Q from django.db.models import Q
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
from . import fields from . import fields
from .relationship import UserBlocks from .relationship import UserBlocks
@ -17,7 +17,7 @@ class Group(BookWyrmModel):
def get_remote_id(self): def get_remote_id(self):
"""don't want the user to be in there in this case""" """don't want the user to be in there in this case"""
return f"https://{DOMAIN}/group/{self.id}" return f"{BASE_URL}/group/{self.id}"
@classmethod @classmethod
def followers_filter(cls, queryset, viewer): def followers_filter(cls, queryset, viewer):

View file

@ -38,7 +38,7 @@ class Link(ActivitypubMixin, BookWyrmModel):
"""create a link""" """create a link"""
# get or create the associated domain # get or create the associated domain
if not self.domain: if not self.domain:
domain = urlparse(self.url).netloc domain = urlparse(self.url).hostname
self.domain, _ = LinkDomain.objects.get_or_create(domain=domain) self.domain, _ = LinkDomain.objects.get_or_create(domain=domain)
# this is never broadcast, the owning model broadcasts an update # this is never broadcast, the owning model broadcasts an update

View file

@ -7,7 +7,7 @@ from django.db.models import Q
from django.utils import timezone from django.utils import timezone
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
@ -50,7 +50,7 @@ class List(OrderedCollectionMixin, BookWyrmModel):
def get_remote_id(self): def get_remote_id(self):
"""don't want the user to be in there in this case""" """don't want the user to be in there in this case"""
return f"https://{DOMAIN}/list/{self.id}" return f"{BASE_URL}/list/{self.id}"
@property @property
def collection_queryset(self): def collection_queryset(self):

View file

@ -3,7 +3,7 @@ from django.core.exceptions import PermissionDenied
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
@ -46,7 +46,7 @@ class Report(BookWyrmModel):
raise PermissionDenied() raise PermissionDenied()
def get_remote_id(self): def get_remote_id(self):
return f"https://{DOMAIN}/settings/reports/{self.id}" return f"{BASE_URL}/settings/reports/{self.id}"
def comment(self, user, note): def comment(self, user, note):
"""comment on a report""" """comment on a report"""

View file

@ -6,7 +6,7 @@ from django.db import models
from django.utils import timezone from django.utils import timezone
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
from bookwyrm.tasks import BROADCAST from bookwyrm.tasks import BROADCAST
from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
@ -71,7 +71,7 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
@property @property
def local_path(self): def local_path(self):
"""No slugs""" """No slugs"""
return self.get_remote_id().replace(f"https://{DOMAIN}", "") return self.get_remote_id().replace(BASE_URL, "")
def raise_not_deletable(self, viewer): def raise_not_deletable(self, viewer):
"""don't let anyone delete a default shelf""" """don't let anyone delete a default shelf"""

View file

@ -12,7 +12,7 @@ from model_utils import FieldTracker
from bookwyrm.connectors.abstract_connector import get_data from bookwyrm.connectors.abstract_connector import get_data
from bookwyrm.preview_images import generate_site_preview_image_task from bookwyrm.preview_images import generate_site_preview_image_task
from bookwyrm.settings import DOMAIN, ENABLE_PREVIEW_IMAGES, STATIC_FULL_URL from bookwyrm.settings import BASE_URL, ENABLE_PREVIEW_IMAGES, STATIC_FULL_URL
from bookwyrm.settings import RELEASE_API from bookwyrm.settings import RELEASE_API
from bookwyrm.tasks import app, MISC from bookwyrm.tasks import app, MISC
from .base_model import BookWyrmModel, new_access_code from .base_model import BookWyrmModel, new_access_code
@ -188,7 +188,7 @@ class SiteInvite(models.Model):
@property @property
def link(self): def link(self):
"""formats the invite link""" """formats the invite link"""
return f"https://{DOMAIN}/invite/{self.code}" return f"{BASE_URL}/invite/{self.code}"
class InviteRequest(BookWyrmModel): class InviteRequest(BookWyrmModel):
@ -235,7 +235,7 @@ class PasswordReset(models.Model):
@property @property
def link(self): def link(self):
"""formats the invite link""" """formats the invite link"""
return f"https://{DOMAIN}/password-reset/{self.code}" return f"{BASE_URL}/password-reset/{self.code}"
# pylint: disable=unused-argument # pylint: disable=unused-argument

View file

@ -19,7 +19,7 @@ from bookwyrm.connectors import get_data, ConnectorException
from bookwyrm.models.shelf import Shelf from bookwyrm.models.shelf import Shelf
from bookwyrm.models.status import Status from bookwyrm.models.status import Status
from bookwyrm.preview_images import generate_user_preview_image_task from bookwyrm.preview_images import generate_user_preview_image_task
from bookwyrm.settings import DOMAIN, ENABLE_PREVIEW_IMAGES, USE_HTTPS, LANGUAGES from bookwyrm.settings import BASE_URL, ENABLE_PREVIEW_IMAGES, LANGUAGES
from bookwyrm.signatures import create_key_pair from bookwyrm.signatures import create_key_pair
from bookwyrm.tasks import app, MISC from bookwyrm.tasks import app, MISC
from bookwyrm.utils import regex from bookwyrm.utils import regex
@ -42,12 +42,6 @@ def get_feed_filter_choices():
return [f[0] for f in FeedFilterChoices] return [f[0] for f in FeedFilterChoices]
def site_link():
"""helper for generating links to the site"""
protocol = "https" if USE_HTTPS else "http"
return f"{protocol}://{DOMAIN}"
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods
class User(OrderedCollectionPageMixin, AbstractUser): class User(OrderedCollectionPageMixin, AbstractUser):
"""a user who wants to read books""" """a user who wants to read books"""
@ -214,8 +208,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
@property @property
def confirmation_link(self): def confirmation_link(self):
"""helper for generating confirmation links""" """helper for generating confirmation links"""
link = site_link() return f"{BASE_URL}/confirm-email/{self.confirmation_code}"
return f"{link}/confirm-email/{self.confirmation_code}"
@property @property
def following_link(self): def following_link(self):
@ -349,7 +342,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
if not self.local and not re.match(regex.FULL_USERNAME, self.username): if not self.local and not re.match(regex.FULL_USERNAME, self.username):
# generate a username that uses the domain (webfinger format) # generate a username that uses the domain (webfinger format)
actor_parts = urlparse(self.remote_id) actor_parts = urlparse(self.remote_id)
self.username = f"{self.username}@{actor_parts.netloc}" self.username = f"{self.username}@{actor_parts.hostname}"
# this user already exists, no need to populate fields # this user already exists, no need to populate fields
if not created: if not created:
@ -369,11 +362,10 @@ class User(OrderedCollectionPageMixin, AbstractUser):
with transaction.atomic(): with transaction.atomic():
# populate fields for local users # populate fields for local users
link = site_link() self.remote_id = f"{BASE_URL}/user/{self.localname}"
self.remote_id = f"{link}/user/{self.localname}"
self.followers_url = f"{self.remote_id}/followers" self.followers_url = f"{self.remote_id}/followers"
self.inbox = f"{self.remote_id}/inbox" self.inbox = f"{self.remote_id}/inbox"
self.shared_inbox = f"{link}/inbox" self.shared_inbox = f"{BASE_URL}/inbox"
self.outbox = f"{self.remote_id}/outbox" self.outbox = f"{self.remote_id}/outbox"
# an id needs to be set before we can proceed with related models # an id needs to be set before we can proceed with related models
@ -558,7 +550,7 @@ def set_remote_server(user_id, allow_external_connections=False):
user = User.objects.get(id=user_id) user = User.objects.get(id=user_id)
actor_parts = urlparse(user.remote_id) actor_parts = urlparse(user.remote_id)
federated_server = get_or_create_remote_server( federated_server = get_or_create_remote_server(
actor_parts.netloc, allow_external_connections=allow_external_connections actor_parts.hostname, allow_external_connections=allow_external_connections
) )
# if we were unable to find the server, we need to create a new entry for it # if we were unable to find the server, we need to create a new entry for it
if not federated_server: if not federated_server:

View file

@ -350,28 +350,31 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
USER_AGENT = f"BookWyrm (BookWyrm/{VERSION}; +https://{DOMAIN}/)"
# Imagekit generated thumbnails # Imagekit generated thumbnails
ENABLE_THUMBNAIL_GENERATION = env.bool("ENABLE_THUMBNAIL_GENERATION", False) ENABLE_THUMBNAIL_GENERATION = env.bool("ENABLE_THUMBNAIL_GENERATION", False)
IMAGEKIT_CACHEFILE_DIR = "thumbnails" IMAGEKIT_CACHEFILE_DIR = "thumbnails"
IMAGEKIT_DEFAULT_CACHEFILE_STRATEGY = "bookwyrm.thumbnail_generation.Strategy" IMAGEKIT_DEFAULT_CACHEFILE_STRATEGY = "bookwyrm.thumbnail_generation.Strategy"
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__)) PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
CSP_ADDITIONAL_HOSTS = env.list("CSP_ADDITIONAL_HOSTS", []) CSP_ADDITIONAL_HOSTS = env.list("CSP_ADDITIONAL_HOSTS", [])
# Storage
PROTOCOL = "http" PROTOCOL = "http"
if USE_HTTPS: if USE_HTTPS:
PROTOCOL = "https" PROTOCOL = "https"
SESSION_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True
PORT = env.int("PORT", 443 if USE_HTTPS else 80)
if (USE_HTTPS and PORT == 443) or (not USE_HTTPS and PORT == 80):
NETLOC = DOMAIN
else:
NETLOC = f"{DOMAIN}:{PORT}"
BASE_URL = f"{PROTOCOL}://{NETLOC}"
USER_AGENT = f"BookWyrm (BookWyrm/{VERSION}; +{BASE_URL})"
# Storage
USE_S3 = env.bool("USE_S3", False) USE_S3 = env.bool("USE_S3", False)
USE_AZURE = env.bool("USE_AZURE", False) USE_AZURE = env.bool("USE_AZURE", False)
S3_SIGNED_URL_EXPIRY = env.int("S3_SIGNED_URL_EXPIRY", 900) S3_SIGNED_URL_EXPIRY = env.int("S3_SIGNED_URL_EXPIRY", 900)
@ -440,11 +443,11 @@ elif USE_AZURE:
else: else:
# Static settings # Static settings
STATIC_URL = "/static/" STATIC_URL = "/static/"
STATIC_FULL_URL = f"{PROTOCOL}://{DOMAIN}{STATIC_URL}" STATIC_FULL_URL = BASE_URL + STATIC_URL
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage" STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
# Media settings # Media settings
MEDIA_URL = "/images/" MEDIA_URL = "/images/"
MEDIA_FULL_URL = f"{PROTOCOL}://{DOMAIN}{MEDIA_URL}" MEDIA_FULL_URL = BASE_URL + MEDIA_URL
DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage" DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage"
# Exports settings # Exports settings
EXPORTS_STORAGE = "bookwyrm.storage_backends.ExportsFileStorage" EXPORTS_STORAGE = "bookwyrm.storage_backends.ExportsFileStorage"

View file

@ -2,10 +2,10 @@
<div style="font-family: BlinkMacSystemFont,-apple-system,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Fira Sans','Droid Sans','Helvetica Neue',Helvetica,Arial,sans-serif; border-radius: 6px; background-color: #efefef; max-width: 632px;"> <div style="font-family: BlinkMacSystemFont,-apple-system,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Fira Sans','Droid Sans','Helvetica Neue',Helvetica,Arial,sans-serif; border-radius: 6px; background-color: #efefef; max-width: 632px;">
<div style="padding: 1rem; overflow: auto;"> <div style="padding: 1rem; overflow: auto;">
<div style="float: left; margin-right: 1rem;"> <div style="float: left; margin-right: 1rem;">
<a style="color: #3273dc;" href="https://{{ domain }}" style="text-decoration: none;"><img src="{{ logo }}" alt="logo" loading="lazy" decoding="async"></a> <a style="color: #3273dc;" href="{{ base_url }}" style="text-decoration: none;"><img src="{{ logo }}" alt="logo" loading="lazy" decoding="async"></a>
</div> </div>
<div> <div>
<a style="color: black; text-decoration: none" href="https://{{ domain }}" style="text-decoration: none;"><strong>{{ site_name }}</strong><br> <a style="color: black; text-decoration: none" href="{{ base_url }}" style="text-decoration: none;"><strong>{{ site_name }}</strong><br>
{{ domain }}</a> {{ domain }}</a>
</div> </div>
</div> </div>
@ -18,9 +18,9 @@
</div> </div>
<div style="padding: 1rem; font-size: 0.8rem;"> <div style="padding: 1rem; font-size: 0.8rem;">
<p style="margin: 0; color: #333;">{% blocktrans %}BookWyrm hosted on <a style="color: #3273dc;" href="https://{{ domain }}">{{ site_name }}</a>{% endblocktrans %}</p> <p style="margin: 0; color: #333;">{% blocktrans %}BookWyrm hosted on <a style="color: #3273dc;" href="{{ base_url }}">{{ site_name }}</a>{% endblocktrans %}</p>
{% if user %} {% if user %}
<p style="margin: 0; color: #333;"><a style="color: #3273dc;" href="https://{{ domain }}{% url 'prefs-profile' %}">{% trans "Email preference" %}</a></p> <p style="margin: 0; color: #333;"><a style="color: #3273dc;" href="{{ base_url }}{% url 'prefs-profile' %}">{% trans "Email preference" %}</a></p>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View file

@ -12,6 +12,6 @@
<p> <p>
{% url 'code-of-conduct' as coc_path %} {% url 'code-of-conduct' as coc_path %}
{% url 'about' as about_path %} {% url 'about' as about_path %}
{% blocktrans %}Learn more <a href="https://{{ domain }}{{ about_path }}">about {{ site_name }}</a>.{% endblocktrans %} {% blocktrans %}Learn more <a href="{{ base_url }}{{ about_path }}">about {{ site_name }}</a>.{% endblocktrans %}
</p> </p>
{% endblock %} {% endblock %}

View file

@ -5,6 +5,6 @@
{{ invite_link }} {{ invite_link }}
{% blocktrans %}Learn more about {{ site_name }}:{% endblocktrans %} https://{{ domain }}{% url 'about' %} {% blocktrans %}Learn more about {{ site_name }}:{% endblocktrans %} {{ base_url }}{% url 'about' %}
{% endblock %} {% endblock %}

View file

@ -10,6 +10,6 @@
<Image width="16" height="16" type="image/x-icon">{{ image }}</Image> <Image width="16" height="16" type="image/x-icon">{{ image }}</Image>
<Url <Url
type="text/html" type="text/html"
template="https://{{ DOMAIN }}{% url 'search' %}?q={searchTerms}" template="{{ BASE_URL }}{% url 'search' %}?q={searchTerms}"
/> />
</OpenSearchDescription> </OpenSearchDescription>

View file

@ -120,7 +120,7 @@ def id_to_username(user_id):
"""given an arbitrary remote id, return the username""" """given an arbitrary remote id, return the username"""
if user_id: if user_id:
url = urlparse(user_id) url = urlparse(user_id)
domain = url.netloc domain = url.hostname
parts = url.path.split("/") parts = url.path.split("/")
name = parts[-1] name = parts[-1]
value = f"{name}@{domain}" value = f"{name}@{domain}"

View file

@ -6,7 +6,7 @@ import responses
from bookwyrm import models from bookwyrm import models
from bookwyrm.connectors import abstract_connector, ConnectorException from bookwyrm.connectors import abstract_connector, ConnectorException
from bookwyrm.connectors.abstract_connector import Mapping, get_data from bookwyrm.connectors.abstract_connector import Mapping, get_data
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
class AbstractConnector(TestCase): class AbstractConnector(TestCase):
@ -86,7 +86,7 @@ class AbstractConnector(TestCase):
def test_get_or_create_book_existing(self): def test_get_or_create_book_existing(self):
"""find an existing book by remote/origin id""" """find an existing book by remote/origin id"""
self.assertEqual(models.Book.objects.count(), 1) self.assertEqual(models.Book.objects.count(), 1)
self.assertEqual(self.book.remote_id, f"https://{DOMAIN}/book/{self.book.id}") self.assertEqual(self.book.remote_id, f"{BASE_URL}/book/{self.book.id}")
self.assertEqual(self.book.origin_id, "https://example.com/book/1234") self.assertEqual(self.book.origin_id, "https://example.com/book/1234")
# dedupe by origin id # dedupe by origin id
@ -95,9 +95,7 @@ class AbstractConnector(TestCase):
self.assertEqual(result, self.book) self.assertEqual(result, self.book)
# dedupe by remote id # dedupe by remote id
result = self.connector.get_or_create_book( result = self.connector.get_or_create_book(f"{BASE_URL}/book/{self.book.id}")
f"https://{DOMAIN}/book/{self.book.id}"
)
self.assertEqual(models.Book.objects.count(), 1) self.assertEqual(models.Book.objects.count(), 1)
self.assertEqual(result, self.book) self.assertEqual(result, self.book)

View file

@ -29,7 +29,9 @@
"bookwyrmUser": true, "bookwyrmUser": true,
"manuallyApprovesFollowers": false, "manuallyApprovesFollowers": false,
"discoverable": false, "discoverable": false,
"alsoKnownAs": ["https://your.domain.here/user/rat"], "alsoKnownAs": [
"https://your.domain.here:4242/user/rat"
],
"devices": "https://friend.camp/users/tripofmice/collections/devices", "devices": "https://friend.camp/users/tripofmice/collections/devices",
"tag": [], "tag": [],
"icon": { "icon": {
@ -37,4 +39,4 @@
"mediaType": "image/png", "mediaType": "image/png",
"url": "https://example.com/images/avatars/AL-2-crop-50.png" "url": "https://example.com/images/avatars/AL-2-crop-50.png"
} }
} }

View file

@ -214,7 +214,7 @@
"attributedTo": "https://www.example.com//user/rat", "attributedTo": "https://www.example.com//user/rat",
"content": "<p>I like it</p>", "content": "<p>I like it</p>",
"to": [ "to": [
"https://your.domain.here/user/rat/followers" "https://your.domain.here:4242/user/rat/followers"
], ],
"cc": [], "cc": [],
"replies": { "replies": {
@ -395,7 +395,7 @@
"https://local.lists/9999" "https://local.lists/9999"
], ],
"follows": [ "follows": [
"https://your.domain.here/user/rat" "https://your.domain.here:4242/user/rat"
], ],
"blocks": ["https://your.domain.here/user/badger"] "blocks": ["https://your.domain.here:4242/user/badger"]
} }

View file

@ -5,7 +5,7 @@ from django.test import TestCase
from bookwyrm import models from bookwyrm import models
from bookwyrm.models import base_model from bookwyrm.models import base_model
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
# pylint: disable=attribute-defined-outside-init # pylint: disable=attribute-defined-outside-init
@ -44,14 +44,14 @@ class BaseModel(TestCase):
"""these should be generated""" """these should be generated"""
self.test_model.id = 1 # pylint: disable=invalid-name self.test_model.id = 1 # pylint: disable=invalid-name
expected = self.test_model.get_remote_id() expected = self.test_model.get_remote_id()
self.assertEqual(expected, f"https://{DOMAIN}/bookwyrmtestmodel/1") self.assertEqual(expected, f"{BASE_URL}/bookwyrmtestmodel/1")
def test_remote_id_with_user(self): def test_remote_id_with_user(self):
"""format of remote id when there's a user object""" """format of remote id when there's a user object"""
self.test_model.user = self.local_user self.test_model.user = self.local_user
self.test_model.id = 1 self.test_model.id = 1
expected = self.test_model.get_remote_id() expected = self.test_model.get_remote_id()
self.assertEqual(expected, f"https://{DOMAIN}/user/mouse/bookwyrmtestmodel/1") self.assertEqual(expected, f"{BASE_URL}/user/mouse/bookwyrmtestmodel/1")
def test_set_remote_id(self): def test_set_remote_id(self):
"""this function sets remote ids after creation""" """this function sets remote ids after creation"""
@ -60,7 +60,7 @@ class BaseModel(TestCase):
instance = models.Work.objects.create(title="work title") instance = models.Work.objects.create(title="work title")
instance.remote_id = None instance.remote_id = None
base_model.set_remote_id(None, instance, True) base_model.set_remote_id(None, instance, True)
self.assertEqual(instance.remote_id, f"https://{DOMAIN}/book/{instance.id}") self.assertEqual(instance.remote_id, f"{BASE_URL}/book/{instance.id}")
# shouldn't set remote_id if it's not created # shouldn't set remote_id if it's not created
instance.remote_id = None instance.remote_id = None

View file

@ -31,7 +31,7 @@ class Book(TestCase):
def test_remote_id(self): def test_remote_id(self):
"""fanciness with remote/origin ids""" """fanciness with remote/origin ids"""
remote_id = f"https://{settings.DOMAIN}/book/{self.work.id}" remote_id = f"{settings.BASE_URL}/book/{self.work.id}"
self.assertEqual(self.work.get_remote_id(), remote_id) self.assertEqual(self.work.get_remote_id(), remote_id)
self.assertEqual(self.work.remote_id, remote_id) self.assertEqual(self.work.remote_id, remote_id)

View file

@ -4,7 +4,7 @@ from dataclasses import dataclass
import datetime import datetime
import json import json
import pathlib import pathlib
import re from urllib.parse import urlparse
from typing import List from typing import List
from unittest import expectedFailure from unittest import expectedFailure
from unittest.mock import patch from unittest.mock import patch
@ -22,7 +22,7 @@ from bookwyrm.activitypub.base_activity import ActivityObject
from bookwyrm.models import fields, User, Status, Edition from bookwyrm.models import fields, User, Status, Edition
from bookwyrm.models.base_model import BookWyrmModel from bookwyrm.models.base_model import BookWyrmModel
from bookwyrm.models.activitypub_mixin import ActivitypubMixin from bookwyrm.models.activitypub_mixin import ActivitypubMixin
from bookwyrm.settings import DOMAIN from bookwyrm.settings import PROTOCOL, NETLOC
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@ -427,12 +427,10 @@ class ModelFields(TestCase):
instance = fields.ImageField() instance = fields.ImageField()
output = instance.field_to_activity(user.avatar) output = instance.field_to_activity(user.avatar)
self.assertIsNotNone( parsed_url = urlparse(output.url)
re.match( self.assertEqual(parsed_url.scheme, PROTOCOL)
rf"https:\/\/{DOMAIN}\/.*\.jpg", self.assertEqual(parsed_url.netloc, NETLOC)
output.url, self.assertRegex(parsed_url.path, r"\.jpg$")
)
)
self.assertEqual(output.name, "") self.assertEqual(output.name, "")
self.assertEqual(output.type, "Image") self.assertEqual(output.type, "Image")

View file

@ -28,7 +28,7 @@ class List(TestCase):
def test_remote_id(self, *_): def test_remote_id(self, *_):
"""shelves use custom remote ids""" """shelves use custom remote ids"""
book_list = models.List.objects.create(name="Test List", user=self.local_user) book_list = models.List.objects.create(name="Test List", user=self.local_user)
expected_id = f"https://{settings.DOMAIN}/list/{book_list.id}" expected_id = f"{settings.BASE_URL}/list/{book_list.id}"
self.assertEqual(book_list.get_remote_id(), expected_id) self.assertEqual(book_list.get_remote_id(), expected_id)
def test_to_activity(self, *_): def test_to_activity(self, *_):

View file

@ -35,7 +35,7 @@ class Shelf(TestCase):
shelf = models.Shelf.objects.create( shelf = models.Shelf.objects.create(
name="Test Shelf", identifier="test-shelf", user=self.local_user name="Test Shelf", identifier="test-shelf", user=self.local_user
) )
expected_id = f"https://{settings.DOMAIN}/user/mouse/books/test-shelf" expected_id = f"{settings.BASE_URL}/user/mouse/books/test-shelf"
self.assertEqual(shelf.get_remote_id(), expected_id) self.assertEqual(shelf.get_remote_id(), expected_id)
def test_to_activity(self, *_): def test_to_activity(self, *_):

View file

@ -79,7 +79,7 @@ class SiteModels(TestCase):
def test_site_invite_link(self): def test_site_invite_link(self):
"""invite link generator""" """invite link generator"""
invite = models.SiteInvite.objects.create(user=self.local_user, code="hello") invite = models.SiteInvite.objects.create(user=self.local_user, code="hello")
self.assertEqual(invite.link, f"https://{settings.DOMAIN}/invite/hello") self.assertEqual(invite.link, f"{settings.BASE_URL}/invite/hello")
def test_invite_request(self): def test_invite_request(self):
"""someone wants an invite""" """someone wants an invite"""
@ -95,7 +95,7 @@ class SiteModels(TestCase):
"""password reset token""" """password reset token"""
token = models.PasswordReset.objects.create(user=self.local_user, code="hello") token = models.PasswordReset.objects.create(user=self.local_user, code="hello")
self.assertTrue(token.valid()) self.assertTrue(token.valid())
self.assertEqual(token.link, f"https://{settings.DOMAIN}/password-reset/hello") self.assertEqual(token.link, f"{settings.BASE_URL}/password-reset/hello")
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.suggested_users.remove_user_task.delay") @patch("bookwyrm.suggested_users.remove_user_task.delay")

View file

@ -60,7 +60,7 @@ class Status(TestCase):
def test_status_generated_fields(self, *_): def test_status_generated_fields(self, *_):
"""setting remote id""" """setting remote id"""
status = models.Status.objects.create(content="bleh", user=self.local_user) status = models.Status.objects.create(content="bleh", user=self.local_user)
expected_id = f"https://{settings.DOMAIN}/user/mouse/status/{status.id}" expected_id = f"{settings.BASE_URL}/user/mouse/status/{status.id}"
self.assertEqual(status.remote_id, expected_id) self.assertEqual(status.remote_id, expected_id)
self.assertEqual(status.privacy, "public") self.assertEqual(status.privacy, "public")
@ -151,7 +151,7 @@ class Status(TestCase):
self.assertEqual(activity["tag"][0]["type"], "Hashtag") self.assertEqual(activity["tag"][0]["type"], "Hashtag")
self.assertEqual(activity["tag"][0]["name"], "#content") self.assertEqual(activity["tag"][0]["name"], "#content")
self.assertEqual( self.assertEqual(
activity["tag"][0]["href"], f"https://{settings.DOMAIN}/hashtag/{tag.id}" activity["tag"][0]["href"], f"{settings.BASE_URL}/hashtag/{tag.id}"
) )
def test_status_with_mention_to_activity(self, *_): def test_status_with_mention_to_activity(self, *_):
@ -227,11 +227,9 @@ class Status(TestCase):
self.assertEqual(activity["sensitive"], False) self.assertEqual(activity["sensitive"], False)
self.assertIsInstance(activity["attachment"], list) self.assertIsInstance(activity["attachment"], list)
self.assertEqual(activity["attachment"][0]["type"], "Document") self.assertEqual(activity["attachment"][0]["type"], "Document")
self.assertTrue( self.assertRegex(
re.match( activity["attachment"][0]["url"],
r"https:\/\/your.domain.here\/images\/covers\/test(_[A-z0-9]+)?.jpg", rf"^{settings.BASE_URL}/images/covers/test(_[A-z0-9]+)?.jpg$",
activity["attachment"][0]["url"],
)
) )
self.assertEqual(activity["attachment"][0]["name"], "Test Edition") self.assertEqual(activity["attachment"][0]["name"], "Test Edition")
@ -263,12 +261,10 @@ class Status(TestCase):
), ),
) )
self.assertEqual(activity["attachment"][0]["type"], "Document") self.assertEqual(activity["attachment"][0]["type"], "Document")
# self.assertTrue( self.assertRegex(
# re.match( activity["attachment"][0]["url"],
# r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", rf"^{settings.BASE_URL}/images/covers/test_[A-z0-9]+.jpg$",
# activity["attachment"][0].url, )
# )
# )
self.assertEqual(activity["attachment"][0]["name"], "Test Edition") self.assertEqual(activity["attachment"][0]["name"], "Test Edition")
def test_quotation_to_activity(self, *_): def test_quotation_to_activity(self, *_):
@ -306,11 +302,9 @@ class Status(TestCase):
), ),
) )
self.assertEqual(activity["attachment"][0]["type"], "Document") self.assertEqual(activity["attachment"][0]["type"], "Document")
self.assertTrue( self.assertRegex(
re.match( activity["attachment"][0]["url"],
r"https:\/\/your.domain.here\/images\/covers\/test(_[A-z0-9]+)?.jpg", rf"^{settings.BASE_URL}/images/covers/test(_[A-z0-9]+)?.jpg$",
activity["attachment"][0]["url"],
)
) )
self.assertEqual(activity["attachment"][0]["name"], "Test Edition") self.assertEqual(activity["attachment"][0]["name"], "Test Edition")
@ -400,11 +394,9 @@ class Status(TestCase):
) )
self.assertEqual(activity["content"], "test content") self.assertEqual(activity["content"], "test content")
self.assertEqual(activity["attachment"][0]["type"], "Document") self.assertEqual(activity["attachment"][0]["type"], "Document")
self.assertTrue( self.assertRegex(
re.match( activity["attachment"][0]["url"],
r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", rf"^{settings.BASE_URL}/images/covers/test_[A-z0-9]+.jpg$",
activity["attachment"][0]["url"],
)
) )
self.assertEqual(activity["attachment"][0]["name"], "Test Edition") self.assertEqual(activity["attachment"][0]["name"], "Test Edition")
@ -425,11 +417,9 @@ class Status(TestCase):
) )
self.assertEqual(activity["content"], "test content") self.assertEqual(activity["content"], "test content")
self.assertEqual(activity["attachment"][0]["type"], "Document") self.assertEqual(activity["attachment"][0]["type"], "Document")
self.assertTrue( self.assertRegex(
re.match( activity["attachment"][0]["url"],
r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", rf"^{settings.BASE_URL}/images/covers/test_[A-z0-9]+.jpg$",
activity["attachment"][0]["url"],
)
) )
self.assertEqual(activity["attachment"][0]["name"], "Test Edition") self.assertEqual(activity["attachment"][0]["name"], "Test Edition")
@ -448,11 +438,9 @@ class Status(TestCase):
f'rated <em><a href="{self.book.remote_id}">{self.book.title}</a></em>: 3 stars', f'rated <em><a href="{self.book.remote_id}">{self.book.title}</a></em>: 3 stars',
) )
self.assertEqual(activity["attachment"][0]["type"], "Document") self.assertEqual(activity["attachment"][0]["type"], "Document")
self.assertTrue( self.assertRegex(
re.match( activity["attachment"][0]["url"],
r"https:\/\/your.domain.here\/images\/covers\/test_[A-z0-9]+.jpg", rf"^{settings.BASE_URL}/images/covers/test_[A-z0-9]+.jpg$",
activity["attachment"][0]["url"],
)
) )
self.assertEqual(activity["attachment"][0]["name"], "Test Edition") self.assertEqual(activity["attachment"][0]["name"], "Test Edition")

View file

@ -9,15 +9,12 @@ import responses
from bookwyrm import models from bookwyrm import models
from bookwyrm.management.commands import initdb from bookwyrm.management.commands import initdb
from bookwyrm.settings import USE_HTTPS, DOMAIN from bookwyrm.settings import DOMAIN, BASE_URL
# pylint: disable=missing-class-docstring # pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring # pylint: disable=missing-function-docstring
class User(TestCase): class User(TestCase):
protocol = "https://" if USE_HTTPS else "http://"
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
with ( with (
@ -49,11 +46,11 @@ class User(TestCase):
def test_computed_fields(self): def test_computed_fields(self):
"""username instead of id here""" """username instead of id here"""
expected_id = f"{self.protocol}{DOMAIN}/user/mouse" expected_id = f"{BASE_URL}/user/mouse"
self.assertEqual(self.user.remote_id, expected_id) self.assertEqual(self.user.remote_id, expected_id)
self.assertEqual(self.user.username, f"mouse@{DOMAIN}") self.assertEqual(self.user.username, f"mouse@{DOMAIN}")
self.assertEqual(self.user.localname, "mouse") self.assertEqual(self.user.localname, "mouse")
self.assertEqual(self.user.shared_inbox, f"{self.protocol}{DOMAIN}/inbox") self.assertEqual(self.user.shared_inbox, f"{BASE_URL}/inbox")
self.assertEqual(self.user.inbox, f"{expected_id}/inbox") self.assertEqual(self.user.inbox, f"{expected_id}/inbox")
self.assertEqual(self.user.outbox, f"{expected_id}/outbox") self.assertEqual(self.user.outbox, f"{expected_id}/outbox")
self.assertEqual(self.user.followers_url, f"{expected_id}/followers") self.assertEqual(self.user.followers_url, f"{expected_id}/followers")
@ -130,7 +127,7 @@ class User(TestCase):
patch("bookwyrm.lists_stream.populate_lists_task.delay"), patch("bookwyrm.lists_stream.populate_lists_task.delay"),
): ):
user = models.User.objects.create_user( user = models.User.objects.create_user(
f"test2{DOMAIN}", "test2",
"test2@bookwyrm.test", "test2@bookwyrm.test",
localname="test2", localname="test2",
**user_attrs, **user_attrs,
@ -145,7 +142,7 @@ class User(TestCase):
patch("bookwyrm.lists_stream.populate_lists_task.delay"), patch("bookwyrm.lists_stream.populate_lists_task.delay"),
): ):
user = models.User.objects.create_user( user = models.User.objects.create_user(
f"test1{DOMAIN}", "test1",
"test1@bookwyrm.test", "test1@bookwyrm.test",
localname="test1", localname="test1",
**user_attrs, **user_attrs,

View file

@ -15,7 +15,7 @@ from django.utils.http import http_date
from bookwyrm import models from bookwyrm import models
from bookwyrm.activitypub import Follow from bookwyrm.activitypub import Follow
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN, NETLOC
from bookwyrm.signatures import create_key_pair, make_signature, make_digest from bookwyrm.signatures import create_key_pair, make_signature, make_digest
@ -77,7 +77,7 @@ class Signature(TestCase):
"HTTP_SIGNATURE": signature, "HTTP_SIGNATURE": signature,
"HTTP_DIGEST": digest, "HTTP_DIGEST": digest,
"HTTP_CONTENT_TYPE": "application/activity+json; charset=utf-8", "HTTP_CONTENT_TYPE": "application/activity+json; charset=utf-8",
"HTTP_HOST": DOMAIN, "HTTP_HOST": NETLOC,
}, },
) )

View file

@ -2,6 +2,7 @@
import re import re
from django.test import TestCase from django.test import TestCase
from bookwyrm.settings import BASE_URL
from bookwyrm.utils import regex from bookwyrm.utils import regex
from bookwyrm.utils.validate import validate_url_domain from bookwyrm.utils.validate import validate_url_domain
@ -15,17 +16,11 @@ class TestUtils(TestCase):
def test_valid_url_domain(self): def test_valid_url_domain(self):
"""Check with a valid URL""" """Check with a valid URL"""
self.assertEqual( legit = f"{BASE_URL}/legit-book-url/"
validate_url_domain("https://your.domain.here/legit-book-url/"), self.assertEqual(validate_url_domain(legit), legit)
"https://your.domain.here/legit-book-url/",
)
def test_invalid_url_domain(self): def test_invalid_url_domain(self):
"""Check with an invalid URL""" """Check with an invalid URL"""
self.assertIsNone( self.assertIsNone(
validate_url_domain("https://up-to-no-good.tld/bad-actor.exe") validate_url_domain("https://up-to-no-good.tld/bad-actor.exe")
) )
def test_default_url_domain(self):
"""Check with a default URL"""
self.assertEqual(validate_url_domain("/"), "/")

View file

@ -27,7 +27,6 @@ class ViewsHelpers(TestCase):
patch("bookwyrm.lists_stream.populate_lists_task.delay"), patch("bookwyrm.lists_stream.populate_lists_task.delay"),
patch("bookwyrm.suggested_users.rerank_user_task.delay"), patch("bookwyrm.suggested_users.rerank_user_task.delay"),
): ):
cls.local_user = models.User.objects.create_user( cls.local_user = models.User.objects.create_user(
"rat", "rat",
"rat@rat.com", "rat@rat.com",
@ -35,7 +34,6 @@ class ViewsHelpers(TestCase):
local=True, local=True,
discoverable=True, discoverable=True,
localname="rat", localname="rat",
remote_id="https://your.domain.here/user/rat",
) )
with ( with (

View file

@ -8,7 +8,7 @@ from django.test.client import RequestFactory
import responses import responses
from bookwyrm import models, views from bookwyrm import models, views
from bookwyrm.settings import USER_AGENT, DOMAIN from bookwyrm.settings import USER_AGENT, BASE_URL
@patch("bookwyrm.activitystreams.add_status_task.delay") @patch("bookwyrm.activitystreams.add_status_task.delay")
@ -288,13 +288,13 @@ class ViewsHelpers(TestCase): # pylint: disable=too-many-public-methods
def test_redirect_to_referer_valid_domain(self, *_): def test_redirect_to_referer_valid_domain(self, *_):
"""redirect to within the app""" """redirect to within the app"""
request = self.factory.get("/path") request = self.factory.get("/path")
request.META = {"HTTP_REFERER": f"https://{DOMAIN}/and/a/path"} request.META = {"HTTP_REFERER": f"{BASE_URL}/and/a/path"}
result = views.helpers.redirect_to_referer(request) result = views.helpers.redirect_to_referer(request)
self.assertEqual(result.url, f"https://{DOMAIN}/and/a/path") self.assertEqual(result.url, f"{BASE_URL}/and/a/path")
def test_redirect_to_referer_with_get_args(self, *_): def test_redirect_to_referer_with_get_args(self, *_):
"""if the path has get params (like sort) they are preserved""" """if the path has get params (like sort) they are preserved"""
request = self.factory.get("/path") request = self.factory.get("/path")
request.META = {"HTTP_REFERER": f"https://{DOMAIN}/and/a/path?sort=hello"} request.META = {"HTTP_REFERER": f"{BASE_URL}/and/a/path?sort=hello"}
result = views.helpers.redirect_to_referer(request) result = views.helpers.redirect_to_referer(request)
self.assertEqual(result.url, f"https://{DOMAIN}/and/a/path?sort=hello") self.assertEqual(result.url, f"{BASE_URL}/and/a/path?sort=hello")

View file

@ -8,7 +8,7 @@ from django.test.client import RequestFactory
from bookwyrm import models, views from bookwyrm import models, views
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
class IsbnViews(TestCase): class IsbnViews(TestCase):
@ -55,7 +55,7 @@ class IsbnViews(TestCase):
data = json.loads(response.content) data = json.loads(response.content)
self.assertEqual(len(data), 1) self.assertEqual(len(data), 1)
self.assertEqual(data[0]["title"], "Test Book") self.assertEqual(data[0]["title"], "Test Book")
self.assertEqual(data[0]["key"], f"https://{DOMAIN}/book/{self.book.id}") self.assertEqual(data[0]["key"], f"{BASE_URL}/book/{self.book.id}")
def test_isbn_html_response(self): def test_isbn_html_response(self):
"""searches local data only and returns book data in json format""" """searches local data only and returns book data in json format"""

View file

@ -10,7 +10,7 @@ from django.test.client import RequestFactory
from bookwyrm import models, views from bookwyrm import models, views
from bookwyrm.book_search import SearchResult from bookwyrm.book_search import SearchResult
from bookwyrm.settings import DOMAIN from bookwyrm.settings import BASE_URL
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
@ -57,7 +57,7 @@ class Views(TestCase):
data = json.loads(response.content) data = json.loads(response.content)
self.assertEqual(len(data), 1) self.assertEqual(len(data), 1)
self.assertEqual(data[0]["title"], "Test Book") self.assertEqual(data[0]["title"], "Test Book")
self.assertEqual(data[0]["key"], f"https://{DOMAIN}/book/{self.book.id}") self.assertEqual(data[0]["key"], f"{BASE_URL}/book/{self.book.id}")
def test_search_no_query(self): def test_search_no_query(self):
"""just the search page""" """just the search page"""

View file

@ -1,21 +1,15 @@
"""Validations""" """Validations"""
from typing import Optional from typing import Optional
from bookwyrm.settings import DOMAIN, USE_HTTPS from bookwyrm.settings import BASE_URL
def validate_url_domain(url: str) -> Optional[str]: def validate_url_domain(url: Optional[str]) -> Optional[str]:
"""Basic check that the URL starts with the instance domain name""" """Basic check that the URL starts with the instance domain name"""
if not url: if url is None:
return None return None
if url == "/": if not url.startswith(BASE_URL):
return url return None
protocol = "https://" if USE_HTTPS else "http://" return url
origin = f"{protocol}{DOMAIN}"
if url.startswith(origin):
return url
return None

View file

@ -9,7 +9,7 @@ from django.utils import timezone
from django.views.decorators.http import require_GET from django.views.decorators.http import require_GET
from bookwyrm import models from bookwyrm import models
from bookwyrm.settings import DOMAIN, VERSION, LANGUAGE_CODE from bookwyrm.settings import BASE_URL, DOMAIN, VERSION, LANGUAGE_CODE
@require_GET @require_GET
@ -34,7 +34,7 @@ def webfinger(request):
}, },
{ {
"rel": "http://ostatus.org/schema/1.0/subscribe", "rel": "http://ostatus.org/schema/1.0/subscribe",
"template": f"https://{DOMAIN}/ostatus_subscribe?acct={{uri}}", "template": f"{BASE_URL}/ostatus_subscribe?acct={{uri}}",
}, },
], ],
} }

View file

@ -11,19 +11,20 @@ env =
DEBUG = false DEBUG = false
USE_HTTPS = true USE_HTTPS = true
DOMAIN = your.domain.here DOMAIN = your.domain.here
PORT = 4242
ALLOWED_HOSTS = your.domain.here ALLOWED_HOSTS = your.domain.here
BOOKWYRM_DATABASE_BACKEND = postgres BOOKWYRM_DATABASE_BACKEND = postgres
MEDIA_ROOT = images/ MEDIA_ROOT = images/
CELERY_BROKER = "" CELERY_BROKER =
REDIS_BROKER_PORT = 6379 REDIS_BROKER_PORT = 6379
REDIS_BROKER_PASSWORD = beep REDIS_BROKER_PASSWORD = beep
REDIS_ACTIVITY_PORT = 6379 REDIS_ACTIVITY_PORT = 6379
REDIS_ACTIVITY_PASSWORD = beep REDIS_ACTIVITY_PASSWORD = beep
USE_DUMMY_CACHE = true USE_DUMMY_CACHE = true
FLOWER_PORT = 8888 FLOWER_PORT = 8888
EMAIL_HOST = "smtp.mailgun.org" EMAIL_HOST = smtp.mailgun.org
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_HOST_USER = "" EMAIL_HOST_USER =
EMAIL_HOST_PASSWORD = "" EMAIL_HOST_PASSWORD =
EMAIL_USE_TLS = true EMAIL_USE_TLS = true
ENABLE_PREVIEW_IMAGES = false ENABLE_PREVIEW_IMAGES = false