diff --git a/src/backend/InvenTree/InvenTree/api.py b/src/backend/InvenTree/InvenTree/api.py index e2d99dbacb..9b60db5693 100644 --- a/src/backend/InvenTree/InvenTree/api.py +++ b/src/backend/InvenTree/InvenTree/api.py @@ -206,6 +206,7 @@ class InfoApiSerializer(serializers.Serializer): navbar_message = serializers server = serializers.CharField(read_only=True) + id = serializers.CharField(read_only=True) version = serializers.CharField(read_only=True) instance = serializers.CharField(read_only=True) apiVersion = serializers.IntegerField(read_only=True) # noqa: N815 @@ -258,6 +259,7 @@ class InfoView(APIView): data = { 'server': 'InvenTree', + 'id': InvenTree.version.inventree_identifier(), 'version': InvenTree.version.inventreeVersion(), 'instance': InvenTree.version.inventreeInstanceName(), 'apiVersion': InvenTree.version.inventreeApiVersion(), diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index d6b03dbad4..f06d7958e8 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,13 +1,16 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 312 +INVENTREE_API_VERSION = 313 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v313 - 2025-02-17 : https://github.com/inventree/InvenTree/pull/9087 + - Adds instance id optionally to the info view endpoint + v312 - 2025-02-15 : https://github.com/inventree/InvenTree/pull/9079 - Remove old API endpoints associated with legacy BOM import functionality diff --git a/src/backend/InvenTree/InvenTree/tests.py b/src/backend/InvenTree/InvenTree/tests.py index 8a8dedd6c1..bb06fad50f 100644 --- a/src/backend/InvenTree/InvenTree/tests.py +++ b/src/backend/InvenTree/InvenTree/tests.py @@ -30,6 +30,7 @@ import InvenTree.helpers_model import InvenTree.tasks from common.currency import currency_codes from common.models import CustomUnit, InvenTreeSetting +from common.settings import get_global_setting from InvenTree.helpers_mixin import ClassProviderMixin, ClassValidationMixin from InvenTree.sanitizer import sanitize_svg from InvenTree.unit_test import InvenTreeTestCase, in_env_context @@ -1245,6 +1246,18 @@ class TestSettings(InvenTreeTestCase): with in_env_context({TEST_ENV_NAME: "{'a': 1}"}): self.assertEqual(config.get_setting(TEST_ENV_NAME, None, typecast=dict), {}) + def test_instance_id(self): + """Test get_instance_id.""" + val = get_global_setting('INVENTREE_INSTANCE_ID') + self.assertGreater(len(val), 10) + + # version helper + self.assertIsNone(version.inventree_identifier()) + + # with env set + with in_env_context({'INVENTREE_ANNOUNCE_ID': 'True'}): + self.assertEqual(val, version.inventree_identifier()) + class TestInstanceName(InvenTreeTestCase): """Unit tests for instance name.""" diff --git a/src/backend/InvenTree/InvenTree/version.py b/src/backend/InvenTree/InvenTree/version.py index 9aad2e65d8..bd1bbd64c8 100644 --- a/src/backend/InvenTree/InvenTree/version.py +++ b/src/backend/InvenTree/InvenTree/version.py @@ -300,3 +300,14 @@ def inventreeDatabase(): """Return the InvenTree database backend e.g. 'postgresql'.""" db = settings.DATABASES['default'] return db.get('ENGINE', None).replace('django.db.backends.', '') + + +def inventree_identifier(override_announce: bool = False): + """Return the InvenTree instance ID.""" + from common.settings import get_global_setting + + if override_announce or get_global_setting( + 'INVENTREE_ANNOUNCE_ID', enviroment_key='INVENTREE_ANNOUNCE_ID' + ): + return get_global_setting('INVENTREE_INSTANCE_ID', default='') + return None diff --git a/src/backend/InvenTree/common/setting/system.py b/src/backend/InvenTree/common/setting/system.py index 6061ec7a8b..59c329da61 100644 --- a/src/backend/InvenTree/common/setting/system.py +++ b/src/backend/InvenTree/common/setting/system.py @@ -3,6 +3,7 @@ import json import os import re +import uuid from django.conf import settings as django_settings from django.contrib.auth.models import Group @@ -120,6 +121,11 @@ def barcode_plugins() -> list: ] +def default_uuid4() -> str: + """Return a default UUID4 value.""" + return str(uuid.uuid4()) + + class BaseURLValidator(URLValidator): """Validator for the InvenTree base URL. @@ -170,6 +176,20 @@ SYSTEM_SETTINGS: dict[str, InvenTreeSettingsKeyType] = { 'default': 0, 'validator': int, }, + 'INVENTREE_INSTANCE_ID': { + 'name': _('Instance ID'), + 'description': _('Unique identifier for this InvenTree instance'), + 'default': default_uuid4, + 'hidden': True, + }, + 'INVENTREE_ANNOUNCE_ID': { + 'name': _('Announce ID'), + 'description': _( + 'Announce the instance ID of the server in the server status info (unauthenticated)' + ), + 'default': False, + 'validator': bool, + }, 'INVENTREE_INSTANCE': { 'name': _('Server Instance Name'), 'default': 'InvenTree', diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index 394d4f2d8a..ef854f63e3 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -42,6 +42,8 @@ export default function SystemSettings() { keys={[ 'INVENTREE_BASE_URL', 'INVENTREE_COMPANY_NAME', + 'INVENTREE_INSTANCE_ID', + 'INVENTREE_ANNOUNCE_ID', 'INVENTREE_INSTANCE', 'INVENTREE_INSTANCE_TITLE', 'INVENTREE_RESTRICT_ABOUT',