mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-21 17:11:06 +00:00
Allow serving BookWyrm on a non-standard port
This commit is contained in:
parent
baea105c18
commit
3aefbb548e
11 changed files with 38 additions and 27 deletions
11
.env.example
11
.env.example
|
@ -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)
|
||||||
|
|
|
@ -118,9 +118,9 @@ 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}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
connector_info = models.Connector.objects.get(identifier=identifier)
|
connector_info = models.Connector.objects.get(identifier=identifier)
|
||||||
|
@ -188,8 +188,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
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -349,7 +349,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:
|
||||||
|
@ -558,7 +558,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:
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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}"
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ 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/
|
||||||
|
|
Loading…
Reference in a new issue