Add support for context path (#313)

* Add support for context path

add an optional environment variable: LD_CONTEXT_PATH

* Fix for pull request code review comments

Co-authored-by: s2marine <s2marine@gmail.com>
This commit is contained in:
s2marine 2022-08-07 18:41:11 +08:00 committed by GitHub
parent eadae32eb3
commit 8053468ca5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 81 additions and 7 deletions

View file

@ -4,6 +4,9 @@ LD_CONTAINER_NAME=linkding
LD_HOST_PORT=9090
# Directory on the host system that should be mounted as data dir into the Docker container
LD_HOST_DATA_DIR=./data
# Can be used to run linkding under a context path, for example: linkding/
# Must end with a slash `/`
LD_CONTEXT_PATH=
# Option to disable background tasks
LD_DISABLE_BACKGROUND_TASKS=False

View file

@ -44,7 +44,7 @@
{% endif %}
<div class="navbar container grid-lg">
<section class="navbar-section">
<a href="/" class="navbar-brand text-bold">
<a href="{% url 'bookmarks:index' %}" class="navbar-brand text-bold">
<img class="logo" src="{% static 'logo.png' %}" alt="Application logo">
<h1>linkding</h1>
</a>

View file

@ -0,0 +1,53 @@
import importlib
from django.test import TestCase, override_settings
from django.urls import reverse
class MockUrlConf:
def __init__(self, module):
self.urlpatterns = module.urlpatterns
class ContextPathTestCase(TestCase):
def setUp(self):
self.siteroot_urls = importlib.import_module('siteroot.urls')
@override_settings(LD_CONTEXT_PATH=None)
def tearDown(self):
importlib.reload(self.siteroot_urls)
@override_settings(LD_CONTEXT_PATH='linkding/')
def test_route_with_context_path(self):
module = importlib.reload(self.siteroot_urls)
# pass mock config instead of actual module to prevent caching the
# url config in django.urls.reverse
urlconf = MockUrlConf(module)
test_cases = [
('bookmarks:index', '/linkding/bookmarks'),
('bookmarks:bookmark-list', '/linkding/api/bookmarks/'),
('login', '/linkding/login/'),
('admin:bookmarks_bookmark_changelist', '/linkding/admin/bookmarks/bookmark/'),
]
for url_name, expected_url in test_cases:
url = reverse(url_name, urlconf=urlconf)
self.assertEqual(expected_url, url)
@override_settings(LD_CONTEXT_PATH='')
def test_route_without_context_path(self):
module = importlib.reload(self.siteroot_urls)
# pass mock config instead of actual module to prevent caching the
# url config in django.urls.reverse
urlconf = MockUrlConf(module)
test_cases = [
('bookmarks:index', '/bookmarks'),
('bookmarks:bookmark-list', '/api/bookmarks/'),
('login', '/login/'),
('admin:bookmarks_bookmark_changelist', '/admin/bookmarks/bookmark/'),
]
for url_name, expected_url in test_cases:
url = reverse(url_name, urlconf=urlconf)
self.assertEqual(expected_url, url)

View file

@ -73,7 +73,7 @@ def get_ttl_hash(seconds=3600):
@login_required
def integrations(request):
application_url = request.build_absolute_uri("/bookmarks/new")
application_url = request.build_absolute_uri(reverse('bookmarks:new'))
api_token = Token.objects.get_or_create(user=request.user)[0]
feed_token = FeedToken.objects.get_or_create(user=request.user)[0]
all_feed_url = request.build_absolute_uri(reverse('bookmarks:feeds.all', args=[feed_token.key]))

View file

@ -51,3 +51,10 @@ Configures the request timeout in the uwsgi application server. This can be usef
Values: Valid port number | Default = `9090`
Allows to set a custom port for the UWSGI server running in the container. While Docker containers have their own IP address namespace and port collisions are impossible to achieve, there are other container solutions that share one. Podman, for example, runs all containers in a pod under one namespace, which results in every port only being allowed to be assigned once. This option allows to set a custom port in order to avoid collisions with other containers.
### `LD_CONTEXT_PATH`
Values: `String` | Default = None
Allows configuring the context path of the website. Useful for setting up Nginx reverse proxy.
The context path must end with a slash. For example: `linkding/`

View file

@ -107,9 +107,12 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
LOGIN_URL = '/login'
LOGIN_REDIRECT_URL = '/bookmarks'
LOGOUT_REDIRECT_URL = '/login'
# Website context path.
LD_CONTEXT_PATH = os.getenv('LD_CONTEXT_PATH', '')
LOGIN_URL = '/' + LD_CONTEXT_PATH + 'login'
LOGIN_REDIRECT_URL = '/' + LD_CONTEXT_PATH + 'bookmarks'
LOGOUT_REDIRECT_URL = '/' + LD_CONTEXT_PATH + 'login'
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
@ -127,7 +130,7 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
STATIC_URL = '/' + LD_CONTEXT_PATH + 'static/'
# Collect static files in static folder
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

View file

@ -13,6 +13,7 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.contrib.auth import views as auth_views
from django.urls import path, include
@ -30,6 +31,9 @@ urlpatterns = [
path('', include('bookmarks.urls')),
]
if settings.LD_CONTEXT_PATH:
urlpatterns = [path(settings.LD_CONTEXT_PATH, include(urlpatterns))]
if DEBUG:
import debug_toolbar

View file

@ -12,6 +12,10 @@ uid = www-data
gid = www-data
buffer-size = 8192
if-env = LD_CONTEXT_PATH
static-map = /%(_)static=static
endif =
if-env = LD_REQUEST_TIMEOUT
http-timeout = %(_)
socket-timeout = %(_)