2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 19:46:46 +00:00

Base URL configuration options (#4749)

* Improve construct_absolute_url method

- Look for hard-coded site URL if provided
- Otherwise look for specified site URL
- Otherwise look at the provided request object

* Refactor existing code which used base URL setting

* Update docs

* Validate that a provided base URL is valid
This commit is contained in:
Oliver 2023-05-02 22:14:57 +10:00 committed by GitHub
parent 10c3d101e8
commit 61d613ff34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 75 additions and 33 deletions

View File

@ -17,6 +17,7 @@ from django.contrib.staticfiles.storage import StaticFilesStorage
from django.core.exceptions import FieldError, ValidationError from django.core.exceptions import FieldError, ValidationError
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from django.core.validators import URLValidator from django.core.validators import URLValidator
from django.db.utils import OperationalError, ProgrammingError
from django.http import StreamingHttpResponse from django.http import StreamingHttpResponse
from django.test import TestCase from django.test import TestCase
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -88,31 +89,57 @@ def getStaticUrl(filename):
return os.path.join(STATIC_URL, str(filename)) return os.path.join(STATIC_URL, str(filename))
def construct_absolute_url(*arg): def construct_absolute_url(*arg, **kwargs):
"""Construct (or attempt to construct) an absolute URL from a relative URL. """Construct (or attempt to construct) an absolute URL from a relative URL.
This is useful when (for example) sending an email to a user with a link This is useful when (for example) sending an email to a user with a link
to something in the InvenTree web framework. to something in the InvenTree web framework.
This requires the BASE_URL configuration option to be set! A URL is constructed in the following order:
1. If setings.SITE_URL is set (e.g. in the Django settings), use that
2. If the InvenTree setting INVENTREE_BASE_URL is set, use that
3. Otherwise, use the current request URL (if available)
""" """
base = str(common.models.InvenTreeSetting.get_setting('INVENTREE_BASE_URL'))
url = '/'.join(arg) relative_url = '/'.join(arg)
if not base: # If a site URL is provided, use that
return url site_url = getattr(settings, 'SITE_URL', None)
if not site_url:
# Otherwise, try to use the InvenTree setting
try:
site_url = common.models.InvenTreeSetting.get_setting('INVENTREE_BASE_URL', create=False, cache=False)
except ProgrammingError:
pass
except OperationalError:
pass
if not site_url:
# Otherwise, try to use the current request
request = kwargs.get('request', None)
if request:
site_url = request.build_absolute_uri('/')
if not site_url:
# No site URL available, return the relative URL
return relative_url
# Strip trailing slash from base url # Strip trailing slash from base url
if base.endswith('/'): if site_url.endswith('/'):
base = base[:-1] site_url = site_url[:-1]
if url.startswith('/'): if relative_url.startswith('/'):
url = url[1:] relative_url = relative_url[1:]
url = f"{base}/{url}" return f"{site_url}/{relative_url}"
return url
def get_base_url(**kwargs):
"""Return the base URL for the InvenTree server"""
return construct_absolute_url('', **kwargs)
def download_image_from_url(remote_url, timeout=2.5): def download_image_from_url(remote_url, timeout=2.5):

View File

@ -17,6 +17,7 @@ from pathlib import Path
import django.conf.locale import django.conf.locale
import django.core.exceptions import django.core.exceptions
from django.core.validators import URLValidator
from django.http import Http404 from django.http import Http404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -908,6 +909,16 @@ PLUGIN_TESTING_EVENTS = False
PLUGIN_RETRY = get_setting('INVENTREE_PLUGIN_RETRY', 'PLUGIN_RETRY', 5) # How often should plugin loading be tried? PLUGIN_RETRY = get_setting('INVENTREE_PLUGIN_RETRY', 'PLUGIN_RETRY', 5) # How often should plugin loading be tried?
PLUGIN_FILE_CHECKED = False # Was the plugin file checked? PLUGIN_FILE_CHECKED = False # Was the plugin file checked?
# Site URL can be specified statically, or via a run-time setting
SITE_URL = get_setting('INVENTREE_SITE_URL', 'site_url', None)
if SITE_URL:
logger.info(f"Site URL: {SITE_URL}")
# Check that the site URL is valid
validator = URLValidator()
validator(SITE_URL)
# User interface customization values # User interface customization values
CUSTOM_LOGO = get_custom_file('INVENTREE_CUSTOM_LOGO', 'customize.logo', 'custom logo', lookup_media=True) CUSTOM_LOGO = get_custom_file('INVENTREE_CUSTOM_LOGO', 'customize.logo', 'custom logo', lookup_media=True)
CUSTOM_SPLASH = get_custom_file('INVENTREE_CUSTOM_SPLASH', 'customize.splash', 'custom splash') CUSTOM_SPLASH = get_custom_file('INVENTREE_CUSTOM_SPLASH', 'customize.splash', 'custom splash')

View File

@ -71,6 +71,10 @@ language: en-us
# Reference: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones # Reference: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
timezone: UTC timezone: UTC
# Base URL for the InvenTree server
# Use the environment variable INVENTREE_BASE_URL
# base_url: 'http://localhost:8000'
# Base currency code (or use env var INVENTREE_BASE_CURRENCY) # Base currency code (or use env var INVENTREE_BASE_CURRENCY)
base_currency: USD base_currency: USD

View File

@ -13,10 +13,9 @@ from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import common.models
import part.models import part.models
import stock.models import stock.models
from InvenTree.helpers import normalize, validateFilterString from InvenTree.helpers import get_base_url, normalize, validateFilterString
from InvenTree.models import MetadataMixin from InvenTree.models import MetadataMixin
from plugin.registry import registry from plugin.registry import registry
@ -183,7 +182,7 @@ class LabelTemplate(MetadataMixin, models.Model):
context = self.get_context_data(request) context = self.get_context_data(request)
# Add "basic" context data which gets passed to every label # Add "basic" context data which gets passed to every label
context['base_url'] = common.models.InvenTreeSetting.get_setting('INVENTREE_BASE_URL') context['base_url'] = get_base_url(request=request)
context['date'] = datetime.datetime.now().date() context['date'] = datetime.datetime.now().date()
context['datetime'] = datetime.datetime.now() context['datetime'] = datetime.datetime.now()
context['request'] = request context['request'] = request

View File

@ -3,7 +3,6 @@
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.db import transaction from django.db import transaction
from django.db.models import F, Q from django.db.models import F, Q
from django.db.utils import ProgrammingError
from django.http.response import JsonResponse from django.http.response import JsonResponse
from django.urls import include, path, re_path from django.urls import include, path, re_path
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -20,7 +19,8 @@ from company.models import SupplierPart
from InvenTree.api import (APIDownloadMixin, AttachmentMixin, from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
ListCreateDestroyAPIView, MetadataView, StatusView) ListCreateDestroyAPIView, MetadataView, StatusView)
from InvenTree.filters import SEARCH_ORDER_FILTER, SEARCH_ORDER_FILTER_ALIAS from InvenTree.filters import SEARCH_ORDER_FILTER, SEARCH_ORDER_FILTER_ALIAS
from InvenTree.helpers import DownloadFile, str2bool from InvenTree.helpers import (DownloadFile, construct_absolute_url,
get_base_url, str2bool)
from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI,
RetrieveUpdateDestroyAPI) RetrieveUpdateDestroyAPI)
from InvenTree.status_codes import (PurchaseOrderStatus, ReturnOrderLineStatus, from InvenTree.status_codes import (PurchaseOrderStatus, ReturnOrderLineStatus,
@ -1371,11 +1371,8 @@ class OrderCalendarExport(ICalFeed):
whether or not to show completed orders. Defaults to false whether or not to show completed orders. Defaults to false
""" """
try: instance_url = get_base_url()
instance_url = InvenTreeSetting.get_setting('INVENTREE_BASE_URL', create=False, cache=False)
except ProgrammingError: # pragma: no cover
# database is not initialized yet
instance_url = ''
instance_url = instance_url.replace("http://", "").replace("https://", "") instance_url = instance_url.replace("http://", "").replace("https://", "")
timezone = settings.TIME_ZONE timezone = settings.TIME_ZONE
file_name = "calendar.ics" file_name = "calendar.ics"
@ -1507,9 +1504,7 @@ class OrderCalendarExport(ICalFeed):
def item_link(self, item): def item_link(self, item):
"""Set the item link.""" """Set the item link."""
# Do not use instance_url as here, as the protocol needs to be included return construct_absolute_url(item.get_absolute_url())
site_url = InvenTreeSetting.get_setting("INVENTREE_BASE_URL")
return f'{site_url}{item.get_absolute_url()}'
order_api_urls = [ order_api_urls = [

View File

@ -222,8 +222,8 @@ def inventree_splash(**kwargs):
@register.simple_tag() @register.simple_tag()
def inventree_base_url(*args, **kwargs): def inventree_base_url(*args, **kwargs):
"""Return the INVENTREE_BASE_URL setting.""" """Return the base URL of the InvenTree server"""
return InvenTreeSetting.get_setting('INVENTREE_BASE_URL') return InvenTree.helpers.get_base_url()
@register.simple_tag() @register.simple_tag()

View File

@ -20,7 +20,7 @@ import common.models
import order.models import order.models
import part.models import part.models
import stock.models import stock.models
from InvenTree.helpers import validateFilterString from InvenTree.helpers import get_base_url, validateFilterString
from InvenTree.models import MetadataMixin from InvenTree.models import MetadataMixin
from plugin.registry import registry from plugin.registry import registry
@ -203,7 +203,7 @@ class ReportTemplateBase(MetadataMixin, ReportBase):
# Generate custom context data based on the particular report subclass # Generate custom context data based on the particular report subclass
context = self.get_context_data(request) context = self.get_context_data(request)
context['base_url'] = common.models.InvenTreeSetting.get_setting('INVENTREE_BASE_URL') context['base_url'] = get_base_url(request=request)
context['date'] = datetime.datetime.now().date() context['date'] = datetime.datetime.now().date()
context['datetime'] = datetime.datetime.now() context['datetime'] = datetime.datetime.now()
context['default_page_size'] = common.models.InvenTreeSetting.get_setting('REPORT_DEFAULT_PAGE_SIZE') context['default_page_size'] = common.models.InvenTreeSetting.get_setting('REPORT_DEFAULT_PAGE_SIZE')

View File

@ -205,10 +205,7 @@ def logo_image(**kwargs):
def internal_link(link, text): def internal_link(link, text):
"""Make a <a></a> href which points to an InvenTree URL. """Make a <a></a> href which points to an InvenTree URL.
Important Note: This only works if the INVENTREE_BASE_URL parameter is set! Uses the InvenTree.helpers.construct_absolute_url function to build the URL.
If the INVENTREE_BASE_URL parameter is not configured,
the text will be returned (unlinked)
""" """
text = str(text) text = str(text)

View File

@ -56,6 +56,15 @@ The following basic options are available:
| INVENTREE_TIMZONE | timezome | Server timezone | UTC | | INVENTREE_TIMZONE | timezome | Server timezone | UTC |
| ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin | | ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin |
| INVENTREE_LANGUAGE | language | Default language | en-us | | INVENTREE_LANGUAGE | language | Default language | en-us |
| INVENTREE_BASE_URL | base_url | Server base URL | *Not specified* |
### Base URL Configuration
The base URL of the InvenTree site is required for constructing absolute URLs in a number of circumstances. To construct a URL, the InvenTree iterates through the following options in decreasing order of importance:
1. Static configuration (i.e. set using environment variable or configuration file as above)
2. Global settings (i.e. configured at run-time in the [global settings](../settings/global.md))
3. Using the hostname supplied by the user request
## Administrator Account ## Administrator Account