2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-04-25 12:33:33 +00:00

Ensure configuration settings are documented (#11674)

* Export configuration keys to JSON

* Support rendering of config options in docs

* Check for missing config settings

* Simplify macro

* Initial tests

* Fix collisions

* Updates

* Ensure values are stringified

* Ensure null values are exported correctly

* Refactor database config

- Observability on the config settings

* More docs updates

* Updates

* Add observability of cache settings

* More updates

* Observability

* Set env config for RTD

* Revert RTD config file

- Handled by ENV VAR on RTD account

* Visibility on background worker settings

* Tweaks

* Tweaks

* Split tracing settings out into separate file

- Improved discovery
- declutter settings.py

* Cleanup LDAP settings

* Social providers docs

* More updates

* Refactor ldap setup into own module

* Tweaks

* Formatting tweaks

* Tweak logic

* Fix INVENTREE_SESSION_COOKIE_SECURE setting

* Fix wrapping

* Add custom default
This commit is contained in:
Oliver
2026-04-21 12:23:53 +10:00
committed by GitHub
parent 3c9b014939
commit d81a87225c
20 changed files with 754 additions and 635 deletions
+19 -1
View File
@@ -280,9 +280,27 @@ def on_post_build(*args, **kwargs):
ignored_settings = { ignored_settings = {
'global': ['SERVER_RESTART_REQUIRED'], 'global': ['SERVER_RESTART_REQUIRED'],
'user': ['LAST_USED_PRINTING_MACHINES'], 'user': ['LAST_USED_PRINTING_MACHINES'],
'config': [
'INVENTREE_DB_TCP_KEEPALIVES',
'INVENTREE_DB_TCP_KEEPALIVES_IDLE',
'INVENTREE_DB_TCP_KEEPALIVES_INTERVAL',
'INVENTREE_DB_TCP_KEEPALIVES_COUNT',
'INVENTREE_DB_ISOLATION_SERIALIZABLE',
'INVENTREE_DB_WAL_MODE',
'INVENTREE_PLUGIN_DIR',
'INVENTREE_DOCKER',
'INVENTREE_FLAGS',
'INVENTREE_REMOTE_LOGIN',
'INVENTREE_REMOTE_LOGIN_HEADER',
'TEST_TRANSLATIONS',
'INVENTREE_FRONTEND_URL_BASE',
'INVENTREE_FRONTEND_API_HOST',
'INVENTREE_FRONTEND_SETTINGS',
'INVENTREE_LOGOUT_REDIRECT_URL',
],
} }
for group in ['global', 'user']: for group in ['global', 'user', 'config']:
expected = expected_settings.get(group, {}) expected = expected_settings.get(group, {})
observed = observed_settings.get(group, {}) observed = observed_settings.get(group, {})
ignored = ignored_settings.get(group, []) ignored = ignored_settings.get(group, [])
+4 -5
View File
@@ -9,11 +9,10 @@ For complicated plugins it makes sense to add unit tests the code to ensure that
For plugin testing the following environment variables must be set to True: For plugin testing the following environment variables must be set to True:
| Name | Function | Value | {{ configtable() }}
| ---- | -------- | ----- | {{ configsetting("INVENTREE_PLUGINS_ENABLED") }} Enables the use of 3rd party plugins |
| INVENTREE_PLUGINS_ENABLED | Enables the use of 3rd party plugins | True | {{ configsetting("INVENTREE_PLUGIN_TESTING") }} Enables enables all plugins no matter of their active state in the db or built-in flag |
| INVENTREE_PLUGIN_TESTING | Enables enables all plugins no matter of their active state in the db or built-in flag | True | {{ configsetting("INVENTREE_PLUGIN_TESTING_SETUP") }} Enables the url mixin |
| INVENTREE_PLUGIN_TESTING_SETUP | Enables the url mixin | True |
### Test Program ### Test Program
+4 -4
View File
@@ -29,10 +29,10 @@ The first step is to ensure that the required provider modules are installed, vi
There are two variables in the configuration file which define the operation of SSO: There are two variables in the configuration file which define the operation of SSO:
| Environment Variable |Configuration File | Description | More Info | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_SOCIAL_BACKENDS") }} A *list* of [social provider backends](https://docs.allauth.org/en/latest/installation/quickstart.html) enabled for the InvenTree instance |
| INVENTREE_SOCIAL_BACKENDS | `social_backends` | A *list* of provider backends enabled for the InvenTree instance | [django-allauth docs](https://docs.allauth.org/en/latest/installation/quickstart.html) | {{ configsetting("INVENTREE_SOCIAL_PROVIDERS") }} A *dict* of settings specific to the [installed providers](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) |
| INVENTREE_SOCIAL_PROVIDERS | `social_providers` | A *dict* of settings specific to the installed providers | [provider documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) |
In the example below, SSO provider modules are activated for *google*, *github* and *microsoft*. Specific configuration options are specified for the *microsoft* provider module: In the example below, SSO provider modules are activated for *google*, *github* and *microsoft*. Specific configuration options are specified for the *microsoft* provider module:
+35 -38
View File
@@ -51,46 +51,44 @@ You can link your InvenTree server to an LDAP server.
Next you can start configuring the connection. Either use the config file or set the environment variables. Next you can start configuring the connection. Either use the config file or set the environment variables.
| config key | ENV Variable | Description | {{ configtable() }}
| --- | --- | --- | {{ configsetting("INVENTREE_LDAP_ENABLED") }} Enable LDAP support |
| `ldap.enabled` | `INVENTREE_LDAP_ENABLED` | Set this to `True` to enable LDAP. | {{ configsetting("INVENTREE_LDAP_DEBUG") }} Set this to `True` to activate debug mode, useful for troubleshooting ldap configurations. |
| `ldap.debug` | `INVENTREE_LDAP_DEBUG` | Set this to `True` to activate debug mode, useful for troubleshooting ldap configurations. | | `INVENTREE_LDAP_SERVER_URI` | `ldap.server_uri` | LDAP Server URI, e.g. `ldaps://example.org` |
| `ldap.server_uri` | `INVENTREE_LDAP_SERVER_URI` | LDAP Server URI, e.g. `ldaps://example.org` | | `INVENTREE_LDAP_START_TLS` | `ldap.start_tls` | Enable TLS encryption over the standard LDAP port, [see](https://django-auth-ldap.readthedocs.io/en/latest/reference.html#auth-ldap-start-tls). (You can set TLS options via `ldap.global_options`) |
| `ldap.start_tls` | `INVENTREE_LDAP_START_TLS` | Enable TLS encryption over the standard LDAP port, [see](https://django-auth-ldap.readthedocs.io/en/latest/reference.html#auth-ldap-start-tls). (You can set TLS options via `ldap.global_options`) | | `INVENTREE_LDAP_BIND_DN` | `ldap.bind_dn` | LDAP bind dn, e.g. `cn=admin,dc=example,dc=org` |
| `ldap.bind_dn` | `INVENTREE_LDAP_BIND_DN` | LDAP bind dn, e.g. `cn=admin,dc=example,dc=org` | | `INVENTREE_LDAP_BIND_PASSWORD` | `ldap.bind_password` | LDAP bind password |
| `ldap.bind_password` | `INVENTREE_LDAP_BIND_PASSWORD` | LDAP bind password | | `INVENTREE_LDAP_SEARCH_BASE_DN` | `ldap.search_base_dn` | LDAP search base dn, e.g. `cn=Users,dc=example,dc=org` |
| `ldap.search_base_dn` | `INVENTREE_LDAP_SEARCH_BASE_DN` | LDAP search base dn, e.g. `cn=Users,dc=example,dc=org` | | `INVENTREE_LDAP_USER_DN_TEMPLATE` | `ldap.user_dn_template` | use direct bind as auth user, `ldap.bind_dn` and `ldap.bin_password` is not necessary then, e.g. `uid=%(user)s,dc=example,dc=org` |
| `ldap.user_dn_template` | `INVENTREE_LDAP_USER_DN_TEMPLATE` | use direct bind as auth user, `ldap.bind_dn` and `ldap.bin_password` is not necessary then, e.g. `uid=%(user)s,dc=example,dc=org` | | `INVENTREE_LDAP_GLOBAL_OPTIONS` | `ldap.global_options` | set advanced options as dict, e.g. TLS settings. For a list of all available options, see [python-ldap docs](https://www.python-ldap.org/en/latest/reference/ldap.html#ldap-options). (keys and values starting with OPT_ get automatically converted to `python-ldap` keys) |
| `ldap.global_options` | `INVENTREE_LDAP_GLOBAL_OPTIONS` | set advanced options as dict, e.g. TLS settings. For a list of all available options, see [python-ldap docs](https://www.python-ldap.org/en/latest/reference/ldap.html#ldap-options). (keys and values starting with OPT_ get automatically converted to `python-ldap` keys) | | `INVENTREE_LDAP_SEARCH_FILTER_STR`| `ldap.search_filter_str` | LDAP search filter str, default: `uid=%(user)s` |
| `ldap.search_filter_str`| `INVENTREE_LDAP_SEARCH_FILTER_STR` | LDAP search filter str, default: `uid=%(user)s` | | `INVENTREE_LDAP_USER_ATTR_MAP` | `ldap.user_attr_map` | LDAP <-> InvenTree user attribute map, can be json if used as env, in yml directly specify the object. default: `{"first_name": "givenName", "last_name": "sn", "email": "mail"}` |
| `ldap.user_attr_map` | `INVENTREE_LDAP_USER_ATTR_MAP` | LDAP <-> InvenTree user attribute map, can be json if used as env, in yml directly specify the object. default: `{"first_name": "givenName", "last_name": "sn", "email": "mail"}` | | `INVENTREE_LDAP_ALWAYS_UPDATE_USER` | `ldap.always_update_user` | Always update the user on each login, default: `true` |
| `ldap.always_update_user` | `INVENTREE_LDAP_ALWAYS_UPDATE_USER` | Always update the user on each login, default: `true` | | `INVENTREE_LDAP_CACHE_TIMEOUT` | `ldap.cache_timeout` | cache timeout to reduce traffic with LDAP server, default: `3600` (1h) |
| `ldap.cache_timeout` | `INVENTREE_LDAP_CACHE_TIMEOUT` | cache timeout to reduce traffic with LDAP server, default: `3600` (1h) | | `INVENTREE_LDAP_GROUP_SEARCH` | `ldap.group_search` | Base LDAP DN for group searching; required to enable group features |
| `ldap.group_search` | `INVENTREE_LDAP_GROUP_SEARCH` | Base LDAP DN for group searching; required to enable group features | | `INVENTREE_LDAP_GROUP_OBJECT_CLASS` | `ldap.group_object_class` | The string to pass to the LDAP group search `(objectClass=<...>)`, default: `groupOfUniqueNames` |
| `ldap.group_object_class` | `INVENTREE_LDAP_GROUP_OBJECT_CLASS` | The string to pass to the LDAP group search `(objectClass=<...>)`, default: `groupOfUniqueNames` | | `INVENTREE_LDAP_MIRROR_GROUPS` | `ldap.mirror_groups` | If `True`, mirror a user's LDAP group membership in the Django database, default: `False` |
| `ldap.mirror_groups` | `INVENTREE_LDAP_MIRROR_GROUPS` | If `True`, mirror a user's LDAP group membership in the Django database, default: `False` | | `INVENTREE_LDAP_GROUP_TYPE_CLASS` | `ldap.group_type_class` | The group class to be imported from `django_auth_ldap.config` as a string, default: `'GroupOfUniqueNamesType'`|
| `ldap.group_type_class` | `INVENTREE_LDAP_GROUP_TYPE_CLASS` | The group class to be imported from `django_auth_ldap.config` as a string, default: `'GroupOfUniqueNamesType'`| | `INVENTREE_LDAP_GROUP_TYPE_CLASS_ARGS` | `ldap.group_type_class_args` | A `list` of positional args to pass to the LDAP group type class, default `[]` |
| `ldap.group_type_class_args` | `INVENTREE_LDAP_GROUP_TYPE_CLASS_ARGS` | A `list` of positional args to pass to the LDAP group type class, default `[]` | | `INVENTREE_LDAP_GROUP_TYPE_CLASS_KWARGS` | `ldap.group_type_class_kwargs` | A `dict` of keyword args to pass to the LDAP group type class, default `{'name_attr': 'cn'}` |
| `ldap.group_type_class_kwargs` | `INVENTREE_LDAP_GROUP_TYPE_CLASS_KWARGS` | A `dict` of keyword args to pass to the LDAP group type class, default `{'name_attr': 'cn'}` | | `INVENTREE_LDAP_REQUIRE_GROUP` | `ldap.require_group` | If set, users _must_ be in this group to log in to InvenTree |
| `ldap.require_group` | `INVENTREE_LDAP_REQUIRE_GROUP` | If set, users _must_ be in this group to log in to InvenTree | | `INVENTREE_LDAP_DENY_GROUP` | `ldap.deny_group` | If set, users _must not_ be in this group to log in to InvenTree |
| `ldap.deny_group` | `INVENTREE_LDAP_DENY_GROUP` | If set, users _must not_ be in this group to log in to InvenTree | | `INVENTREE_LDAP_USER_FLAGS_BY_GROUP` | `ldap.user_flags_by_group` | LDAP group to InvenTree user flag map, can be json if used as env, in yml directly specify the object. See config template for example, default: `{}` |
| `ldap.user_flags_by_group` | `INVENTREE_LDAP_USER_FLAGS_BY_GROUP` | LDAP group to InvenTree user flag map, can be json if used as env, in yml directly specify the object. See config template for example, default: `{}` |
## Tracing support ## Tracing support
Starting with 0.14.0 InvenTree supports sending traces, logs and metrics to OpenTelemetry compatible endpoints (both HTTP and gRPC). A [list of vendors](https://opentelemetry.io/ecosystem/vendors) is available on the project site. Starting with 0.14.0 InvenTree supports sending traces, logs and metrics to OpenTelemetry compatible endpoints (both HTTP and gRPC). A [list of vendors](https://opentelemetry.io/ecosystem/vendors) is available on the project site.
This can be used to track usage and performance of the InvenTree backend and connected services like databases, caches and more. This can be used to track usage and performance of the InvenTree backend and connected services like databases, caches and more.
| config key | ENV Variable | Description | {{ configtable() }}
| --- | --- | --- | {{ configsetting("INVENTREE_TRACING_ENABLED") }} Enable OpenTelemetry |
| `tracing.enabled` | `INVENTREE_TRACING_ENABLED` | Set this to `True` to enable OpenTelemetry. | {{ configsetting("INVENTREE_TRACING_ENDPOINT") }} General endpoint for information (not specific trace/log url) |
| `tracing.endpoint` | `INVENTREE_TRACING_ENDPOINT` | General endpoint for information (not specific trace/log url) | {{ configsetting("INVENTREE_TRACING_HEADERS") }} HTTP headers that should be send with every request (often used for authentication). Format as a dict. |
| `tracing.headers` | `INVENTREE_TRACING_HEADERS` | HTTP headers that should be send with every request (often used for authentication). Format as a dict. | {{ configsetting("INVENTREE_TRACING_AUTH") }} Auth headers that should be send with every requests (will be encoded to b64 and overwrite auth headers) |
| `tracing.auth.basic` | `INVENTREE_TRACING_AUTH_BASIC` | Auth headers that should be send with every requests (will be encoded to b64 and overwrite auth headers) | {{ configsetting("INVENTREE_TRACING_IS_HTTP") }} Are the endpoints HTTP (True, default) or gRPC (False) |
| `tracing.is_http` | `INVENTREE_TRACING_IS_HTTP` | Are the endpoints HTTP (True, default) or gRPC (False) | {{ configsetting("INVENTREE_TRACING_APPEND_HTTP") }} Append default url routes (v1) to `tracing.endpoint` |
| `tracing.append_http` | `INVENTREE_TRACING_APPEND_HTTP` | Append default url routes (v1) to `tracing.endpoint` | {{ configsetting("INVENTREE_TRACING_CONSOLE") }} Print out all exports (additionally) to the console for debugging. Do not use in production |
| `tracing.console` | `INVENTREE_TRACING_CONSOLE` | Print out all exports (additionally) to the console for debugging. Do not use in production | {{ configsetting("INVENTREE_TRACING_RESOURCES") }} Add additional resources to all exports. This can be used to add custom tags to the traces. Format as a dict. |
| `tracing.resources` | `INVENTREE_TRACING_RESOURCES` | Add additional resources to all exports. This can be used to add custom tags to the traces. Format as a dict. |
## Multi Site Support ## Multi Site Support
@@ -99,7 +97,6 @@ If your InvenTree instance is used in a multi-site environment, you can enable m
!!! tip "Django Documentation" !!! tip "Django Documentation"
For more information on multi-site support, refer to the [Django documentation]({% include "django.html" %}/ref/contrib/sites/). For more information on multi-site support, refer to the [Django documentation]({% include "django.html" %}/ref/contrib/sites/).
| Environment Variable | Config Key | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_SITE_MULTI") }} Enable multiple sites |
| INVENTREE_SITE_MULTI | site_multi | Enable multiple sites | False | {{ configsetting("INVENTREE_SITE_ID") }} Specify a fixed site ID |
| INVENTREE_SITE_ID | site_id | Specify a fixed site ID | _Not specified_ |
+13 -13
View File
@@ -17,19 +17,19 @@ The django-dbbackup library provides [multiple configuration options](https://ar
The following configuration options are available for backup: The following configuration options are available for backup:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_BACKUP_STORAGE") }} Backup storage backend. Refer to the [storage backend documentation](#storage-backend). |
| INVENTREE_BACKUP_STORAGE | backup_storage | Backup storage backend. Refer to the [storage backend documentation](#storage-backend). | django.core.files.storage.FileSystemStorage | {{ configsetting("INVENTREE_BACKUP_DIR") }} Backup storage directory. |
| INVENTREE_BACKUP_DIR | backup_dir | Backup storage directory. | *No default* | {{ configsetting("INVENTREE_BACKUP_OPTIONS") }} Specific options for the selected storage backend (dict) |
| INVENTREE_BACKUP_OPTIONS | backup_options | Specific options for the selected storage backend (dict) | *No default* | {{ configsetting("INVENTREE_BACKUP_CONNECTOR_OPTIONS") }} Specific options for the database connector (dict). Refer to the [database connector options](#database-connector) |
| INVENTREE_BACKUP_CONNECTOR_OPTIONS | backup_connector_options | Specific options for the database connector (dict). Refer to the [database connector options](#database-connector). | *No default* | {{ configsetting("INVENTREE_BACKUP_SEND_EMAIL") }} If True, an email is sent to the site admin when an error occurs during a backup or restore procedure. |
| INVENTREE_BACKUP_SEND_EMAIL | backup_send_email | If True, an email is sent to the site admin when an error occurs during a backup or restore procedure. | False | {{ configsetting("INVENTREE_BACKUP_EMAIL_PREFIX") }} Prefix for the subject line of backup-related emails. |
| INVENTREE_BACKUP_EMAIL_PREFIX | backup_email_prefix | Prefix for the subject line of backup-related emails. | `[InvenTree Backup]` | {{ configsetting("INVENTREE_BACKUP_GPG_RECIPIENT") }} Specify GPG recipient if using encryption for backups. |
| INVENTREE_BACKUP_GPG_RECIPIENT | backup_gpg_recipient | Specify GPG recipient if using encryption for backups. | *No default* | {{ configsetting("INVENTREE_BACKUP_DATE_FORMAT") }} Date format string used to format timestamps in backup filenames. |
| INVENTREE_BACKUP_DATE_FORMAT | backup_date_format | Date format string used to format timestamps in backup filenames. | `%Y-%m-%d-%H%M%S` | {{ configsetting("INVENTREE_BACKUP_DATABASE_FILENAME_TEMPLATE") }} Template string used to generate database backup filenames.
| INVENTREE_BACKUP_DATABASE_FILENAME_TEMPLATE | backup_database_filename_template | Template string used to generate database backup filenames. | `InvenTree-db-{datetime}.{extension}` | {{ configsetting("INVENTREE_BACKUP_MEDIA_FILENAME_TEMPLATE") }} Template string used to generate media backup filenames. |
| INVENTREE_BACKUP_MEDIA_FILENAME_TEMPLATE | backup_media_filename_template | Template string used to generate media backup filenames. | `InvenTree-media-{datetime}.{extension}` | {{ configsetting("INVENTREE_BACKUP_RESTORE_ALLOW_NEWER_VERSION") }} If True, allows restoring a backup created with a newer version of InvenTree. This is dangerous as it can lead to hard-to-debug data loss. |
| INVENTREE_BACKUP_RESTORE_ALLOW_NEWER_VERSION | backup_restore_allow_newer_version | If True, allows restoring a backup created with a newer version of InvenTree. This is dangerous as it can lead to hard-to-debug data loss. | False |
### Storage Backend ### Storage Backend
+158 -168
View File
@@ -46,6 +46,9 @@ Environment variable settings generally use the `INVENTREE_` prefix, and are all
!!! warning "Available Variables" !!! warning "Available Variables"
Some configuration options cannot be set via environment variables. Refer to the documentation below. Some configuration options cannot be set via environment variables. Refer to the documentation below.
!!! info "Environment Variable Priority"
Note that a provided environment variable will override the value provided in the configuration file.
#### List Values #### List Values
To specify a list value in an environment variable, use a comma-separated list. For example, to specify a list of trusted origins: To specify a list value in an environment variable, use a comma-separated list. For example, to specify a list of trusted origins:
@@ -58,14 +61,13 @@ INVENTREE_TRUSTED_ORIGINS='https://inventree.example.com:8443,https://stock.exam
The following basic options are available: The following basic options are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_SITE_URL") }} Specify a fixed site URL |
| INVENTREE_SITE_URL | site_url | Specify a fixed site URL | *Not specified* | {{ configsetting("INVENTREE_TIMEZONE") }} Server timezone |
| INVENTREE_TIMEZONE | timezone | Server timezone | UTC | {{ configsetting("INVENTREE_ADMIN_ENABLED") }} Enable the [django administrator interface]({% include "django.html" %}/ref/contrib/admin/) |
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the [django administrator interface]({% include "django.html" %}/ref/contrib/admin/) | True | {{ configsetting("INVENTREE_ADMIN_URL") }} URL for accessing [admin interface](../settings/admin.md) |
| INVENTREE_ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin | {{ configsetting("INVENTREE_LANGUAGE") }} Default language |
| INVENTREE_LANGUAGE | language | Default language | en-us | {{ configsetting("INVENTREE_AUTO_UPDATE") }} Database migrations will be run automatically |
| INVENTREE_AUTO_UPDATE | auto_update | Database migrations will be run automatically | False |
### Site URL ### Site URL
@@ -89,18 +91,17 @@ With "auto update" enabled, the InvenTree server will automatically apply databa
The following debugging / logging options are available: The following debugging / logging options are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_DEBUG") }} Enable [debug mode](./index.md#debug-mode) |
| INVENTREE_DEBUG | debug | Enable [debug mode](./index.md#debug-mode) | False | {{ configsetting("INVENTREE_DB_LOGGING") }} Enable logging of database messages |
| INVENTREE_DEBUG_QUERYCOUNT | debug_querycount | Enable support for [django-querycount](../develop/index.md#django-querycount) middleware. | False | {{ configsetting("INVENTREE_LOG_LEVEL") }} Set level of logging to terminal |
| INVENTREE_DEBUG_SILK | debug_silk | Enable support for [django-silk](../develop/index.md#django-silk) profiling tool. | False | {{ configsetting("INVENTREE_JSON_LOG") }} Log messages as json |
| INVENTREE_DEBUG_SILK_PROFILING | debug_silk_profiling | Enable detailed profiling in django-silk | False | {{ configsetting("INVENTREE_WRITE_LOG") }} Enable writing of log messages to file at config base |
| INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False | {{ configsetting("INVENTREE_CONSOLE_LOG") }} Enable logging to console |
| INVENTREE_LOG_LEVEL | log_level | Set level of logging to terminal | WARNING | {{ configsetting("INVENTREE_SCHEMA_LEVEL") }} Set level of added schema extensions detail (0-3) 0 = including no additional detail |
| INVENTREE_JSON_LOG | json_log | log as json | False | {{ configsetting("INVENTREE_DEBUG_QUERYCOUNT") }} Enable support for [django-querycount](../develop/index.md#django-querycount) middleware. |
| INVENTREE_WRITE_LOG | write_log | Enable writing of log messages to file at config base | False | {{ configsetting("INVENTREE_DEBUG_SILK") }} Enable support for [django-silk](../develop/index.md#django-silk) profiling tool. |
| INVENTREE_CONSOLE_LOG | console_log | Enable logging to console | True | | `INVENTREE_DEBUG_SILK_PROFILING` | `debug_silk_profiling` | False | Enable detailed profiling in django-silk |
| INVENTREE_SCHEMA_LEVEL | schema.level | Set level of added schema extensions detail (0-3) 0 = including no additional detail | 0 |
### Debug Mode ### Debug Mode
@@ -118,26 +119,24 @@ Depending on how your InvenTree installation is configured, you will need to pay
!!! danger "Not Secure" !!! danger "Not Secure"
Allowing access from any host is not secure, and should be adjusted for your installation. Allowing access from any host is not secure, and should be adjusted for your installation.
!!! info "Environment Variables"
Note that a provided environment variable will override the value provided in the configuration file.
!!! success "INVENTREE_SITE_URL" !!! success "INVENTREE_SITE_URL"
If you have specified the `INVENTREE_SITE_URL`, this will automatically be used as a trusted CSRF and CORS host (see below). If you have specified the `INVENTREE_SITE_URL`, this will automatically be used as a trusted CSRF and CORS host (see below).
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_ALLOWED_HOSTS") }} List of allowed hosts |
| INVENTREE_ALLOWED_HOSTS | allowed_hosts | List of allowed hosts | `*` | {{ configsetting("INVENTREE_TRUSTED_ORIGINS", default="Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list.") }} List of trusted origins. Refer to the [django documentation]({% include "django.html" %}/ref/settings/#csrf-trusted-origins) |
| INVENTREE_TRUSTED_ORIGINS | trusted_origins | List of trusted origins. Refer to the [django documentation]({% include "django.html" %}/ref/settings/#csrf-trusted-origins) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. | {{ configsetting("INVENTREE_CORS_ORIGIN_ALLOW_ALL") }} Allow all remote URLS for CORS checks |
| INVENTREE_CORS_ORIGIN_ALLOW_ALL | cors.allow_all | Allow all remote URLS for CORS checks | `False` | {{ configsetting("INVENTREE_CORS_ORIGIN_WHITELIST", default="Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list.") }} List of whitelisted CORS URLs. Refer to the [django-cors-headers documentation](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr) |
| INVENTREE_CORS_ORIGIN_WHITELIST | cors.whitelist | List of whitelisted CORS URLs. Refer to the [django-cors-headers documentation](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. | {{ configsetting("INVENTREE_CORS_ORIGIN_REGEX") }} List of regular expressions for CORS whitelisted URL patterns |
| INVENTREE_CORS_ORIGIN_REGEX | cors.regex | List of regular expressions for CORS whitelisted URL patterns | *Empty list* | {{ configsetting("INVENTREE_CORS_ALLOW_CREDENTIALS") }} Allow cookies in cross-site requests |
| INVENTREE_CORS_ALLOW_CREDENTIALS | cors.allow_credentials | Allow cookies in cross-site requests | `True` | {{ configsetting("INVENTREE_SITE_LAX_PROTOCOL") }} Ignore protocol mismatches on INVE-E7 site checks |
| INVENTREE_SITE_LAX_PROTOCOL | site_lax_protocol | Ignore protocol mismatches on INVE-E7 site checks | `True` | {{ configsetting("INVENTREE_USE_X_FORWARDED_HOST") }} Use forwarded host header |
| INVENTREE_USE_X_FORWARDED_HOST | use_x_forwarded_host | Use forwarded host header | `False` | {{ configsetting("INVENTREE_USE_X_FORWARDED_PORT") }} Use forwarded port header |
| INVENTREE_USE_X_FORWARDED_PORT | use_x_forwarded_port | Use forwarded port header | `False` | {{ configsetting("INVENTREE_USE_X_FORWARDED_PROTO") }} Use forwarded protocol header |
| INVENTREE_USE_X_FORWARDED_PROTO | use_x_forwarded_proto | Use forwarded protocol header | `False` | | `INVENTREE_X_FORWARDED_PROTO_NAME` | `x_forwarded_proto_name` | `HTTP_X_FORWARDED_PROTO` | Name of the header to use for forwarded protocol information |
| INVENTREE_SESSION_COOKIE_SECURE | cookie.secure | Enforce secure session cookies | `False` | {{ configsetting("INVENTREE_SESSION_COOKIE_SECURE") }} Enforce secure session cookies |
| INVENTREE_COOKIE_SAMESITE | cookie.samesite | Session cookie mode. Must be one of `Strict | Lax | None | False`. Refer to the [mozilla developer docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) and the [django documentation]({% include "django.html" %}/ref/settings/#std-setting-SESSION_COOKIE_SAMESITE) for more information. | False | {{ configsetting("INVENTREE_COOKIE_SAMESITE") }} Session cookie mode. Must be one of `Strict | Lax | None | False`. Refer to the [mozilla developer docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) and the [django documentation]({% include "django.html" %}/ref/settings/#std-setting-SESSION_COOKIE_SAMESITE) for more information. |
### Debug Mode ### Debug Mode
@@ -191,10 +190,9 @@ Django provides a powerful [administrator interface]({% include "django.html" %}
The following admin site configuration options are available: The following admin site configuration options are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_ADMIN_ENABLED") }} Enable the django administrator interface |
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the django administrator interface | True | {{ configsetting("INVENTREE_ADMIN_URL") }} URL for accessing the admin interface |
| INVENTREE_ADMIN_URL | admin_url | URL for accessing the admin interface | admin |
!!! warning "Security" !!! warning "Security"
Changing the admin URL is a simple way to improve security, but it is not a substitute for proper security practices. Changing the admin URL is a simple way to improve security, but it is not a substitute for proper security practices.
@@ -203,12 +201,11 @@ The following admin site configuration options are available:
An administrator account can be specified using the following environment variables: An administrator account can be specified using the following environment variables:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_ADMIN_USER") }} Admin account username |
| INVENTREE_ADMIN_USER | admin_user | Admin account username | *Not specified* | {{ configsetting("INVENTREE_ADMIN_PASSWORD") }} Admin account password |
| INVENTREE_ADMIN_PASSWORD | admin_password | Admin account password | *Not specified* | {{ configsetting("INVENTREE_ADMIN_PASSWORD_FILE") }} Admin account password file |
| INVENTREE_ADMIN_PASSWORD_FILE | admin_password_file | Admin account password file | *Not specified* | {{ configsetting("INVENTREE_ADMIN_EMAIL") }} Admin account email address |
| INVENTREE_ADMIN_EMAIL | admin_email |Admin account email address | *Not specified* |
You can either specify the password directly using `INVENTREE_ADMIN_PASSWORD`, or you can specify a file containing the password using `INVENTREE_ADMIN_PASSWORD_FILE` (this is useful for nix users). You can either specify the password directly using `INVENTREE_ADMIN_PASSWORD`, or you can specify a file containing the password using `INVENTREE_ADMIN_PASSWORD_FILE` (this is useful for nix users).
@@ -238,12 +235,11 @@ A PEM-encoded file containing the oidc private key can be passed via the environ
If not specified via environment variables, the fallback files (automatically generated as part of InvenTree installation) will be used. If not specified via environment variables, the fallback files (automatically generated as part of InvenTree installation) will be used.
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_SECRET_KEY") }} Raw secret key value |
| INVENTREE_SECRET_KEY | secret_key | Raw secret key value | *Not specified* | {{ configsetting("INVENTREE_SECRET_KEY_FILE") }} File containing secret key value |
| INVENTREE_SECRET_KEY_FILE | secret_key_file | File containing secret key value | *Not specified* | {{ configsetting("INVENTREE_OIDC_PRIVATE_KEY") }} Raw private key value |
| INVENTREE_OIDC_PRIVATE_KEY | oidc_private_key | Raw private key value | *Not specified* | {{ configsetting("INVENTREE_OIDC_PRIVATE_KEY_FILE", default="oidc.pem") }} File containing private key value in PEM format |
| INVENTREE_OIDC_PRIVATE_KEY_FILE | oidc_private_key_file | File containing private key value in PEM format | *Not specified* |
## Database Options ## Database Options
@@ -253,14 +249,14 @@ Database options are specified under the *database* heading in the configuration
The following database options can be configured: The following database options can be configured:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_DB_ENGINE") }} Database backend |
| INVENTREE_DB_ENGINE | database.ENGINE | Database backend | *Not specified* | {{ configsetting("INVENTREE_DB_NAME") }} Database name |
| INVENTREE_DB_NAME | database.NAME | Database name | *Not specified* | {{ configsetting("INVENTREE_DB_USER") }} Database username (if required) |
| INVENTREE_DB_USER | database.USER | Database username (if required) | *Not specified* | {{ configsetting("INVENTREE_DB_PASSWORD") }} Database password (if required) |
| INVENTREE_DB_PASSWORD | database.PASSWORD | Database password (if required) | *Not specified* | {{ configsetting("INVENTREE_DB_HOST") }} Database host address (if required) |
| INVENTREE_DB_HOST | database.HOST | Database host address (if required) | *Not specified* | {{ configsetting("INVENTREE_DB_PORT") }} Database host port (if required) |
| INVENTREE_DB_PORT | database.PORT | Database host port (if required) | *Not specified* | {{ configsetting("INVENTREE_DB_OPTIONS") }} Additional database options (as a JSON object) |
!!! tip "Database Password" !!! tip "Database Password"
The value specified for `INVENTREE_DB_PASSWORD` should not contain comma `,` or colon `:` characters, otherwise the connection to the database may fail. The value specified for `INVENTREE_DB_PASSWORD` should not contain comma `,` or colon `:` characters, otherwise the connection to the database may fail.
@@ -269,22 +265,20 @@ The following database options can be configured:
If running with a PostgreSQL database backend, the following additional options are available: If running with a PostgreSQL database backend, the following additional options are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_DB_TIMEOUT", default="2") }} Database connection timeout (s) |
| INVENTREE_DB_TIMEOUT | database.timeout | Database connection timeout (s) | 2 | | `INVENTREE_DB_TCP_KEEPALIVES` | database.tcp_keepalives | 1 | TCP keepalive |
| INVENTREE_DB_TCP_KEEPALIVES | database.tcp_keepalives | TCP keepalive | 1 | | `INVENTREE_DB_TCP_KEEPALIVES_IDLE` | database.tcp_keepalives_idle | 1 | Idle TCP keepalive |
| INVENTREE_DB_TCP_KEEPALIVES_IDLE | database.tcp_keepalives_idle | Idle TCP keepalive | 1 | | `INVENTREE_DB_TCP_KEEPALIVES_INTERVAL` | database.tcp_keepalives_interval | 1| TCP keepalive interval |
| INVENTREE_DB_TCP_KEEPALIVES_INTERVAL | database.tcp_keepalives_interval | TCP keepalive interval | 1| | `INVENTREE_DB_TCP_KEEPALIVES_COUNT` | database.tcp_keepalives_count | 5 | TCP keepalive count |
| INVENTREE_DB_TCP_KEEPALIVES_COUNT | database.tcp_keepalives_count | TCP keepalive count | 5 | | `INVENTREE_DB_ISOLATION_SERIALIZABLE` | database.serializable | False | Database isolation level configured to "serializable" |
| INVENTREE_DB_ISOLATION_SERIALIZABLE | database.serializable | Database isolation level configured to "serializable" | False |
### MySQL Settings ### MySQL Settings
If running with a MySQL database backend, the following additional options are available: If running with a MySQL database backend, the following additional options are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | | `INVENTREE_DB_ISOLATION_SERIALIZABLE` | database.serializable | False | Database isolation level configured to "serializable" |
| INVENTREE_DB_ISOLATION_SERIALIZABLE | database.serializable | Database isolation level configured to "serializable" | False |
### SQLite Settings ### SQLite Settings
@@ -293,10 +287,9 @@ If running with a MySQL database backend, the following additional options are a
If running with a SQLite database backend, the following additional options are available: If running with a SQLite database backend, the following additional options are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_DB_TIMEOUT", default="10") }} Database connection timeout (s) |
| INVENTREE_DB_TIMEOUT | database.timeout | Database connection timeout (s) | 10 | | `INVENTREE_DB_WAL_MODE` | database.wal_mode | True | Enable Write-Ahead Logging (WAL) mode for SQLite databases |
| INVENTREE_DB_WAL_MODE | database.wal_mode | Enable Write-Ahead Logging (WAL) mode for SQLite databases | True |
## Caching ## Caching
@@ -314,21 +307,21 @@ Enabling global caching requires connection to a redis server (which is separate
The following cache settings are available: The following cache settings are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_CACHE_ENABLED") }} Enable redis caching |
| INVENTREE_CACHE_ENABLED | cache.enabled | Enable redis caching | False | {{ configsetting("INVENTREE_CACHE_HOST") }} Cache server host |
| INVENTREE_CACHE_HOST | cache.host | Cache server host | *Not specified* | {{ configsetting("INVENTREE_CACHE_PORT") }} Cache server port |
| INVENTREE_CACHE_PORT | cache.port | Cache server port | 6379 | {{ configsetting("INVENTREE_CACHE_PASSWORD") }} Cache server password |
| INVENTREE_CACHE_PASSWORD | cache.password | Cache server password | none | {{ configsetting("INVENTREE_CACHE_USER") }} Cache server username |
| INVENTREE_CACHE_USER | cache.user | Cache server username | none | {{ configsetting("INVENTREE_CACHE_DB") }} Cache server database index |
| INVENTREE_CACHE_DB | cache.db | Cache server database index | 0 | {{ configsetting("INVENTREE_CACHE_CONNECT_TIMEOUT") }} Cache connection timeout (seconds) |
| INVENTREE_CACHE_CONNECT_TIMEOUT | cache.connect_timeout | Cache connection timeout (seconds) | 3 | {{ configsetting("INVENTREE_CACHE_TIMEOUT") }} Cache timeout (seconds) |
| INVENTREE_CACHE_TIMEOUT | cache.timeout | Cache timeout (seconds) | 3 | {{ configsetting("INVENTREE_CACHE_TCP_KEEPALIVE") }} Cache TCP keepalive |
| INVENTREE_CACHE_TCP_KEEPALIVE | cache.tcp_keepalive | Cache TCP keepalive | True | {{ configsetting("INVENTREE_CACHE_KEEPALIVE_COUNT") }} Cache keepalive count |
| INVENTREE_CACHE_KEEPALIVE_COUNT | cache.keepalive_count | Cache keepalive count | 5 | {{ configsetting("INVENTREE_CACHE_KEEPALIVE_IDLE") }} Cache keepalive idle |
| INVENTREE_CACHE_KEEPALIVE_IDLE | cache.keepalive_idle | Cache keepalive idle | 1 | {{ configsetting("INVENTREE_CACHE_KEEPALIVE_INTERVAL") }} Cache keepalive interval |
| INVENTREE_CACHE_KEEPALIVE_INTERVAL | cache.keepalive_interval | Cache keepalive interval | 1 | {{ configsetting("INVENTREE_CACHE_USER_TIMEOUT") }} Cache user timeout |
| INVENTREE_CACHE_USER_TIMEOUT | cache.user_timeout | Cache user timeout | 1000 |
!!! tip "Cache Password" !!! tip "Cache Password"
The value specified for `INVENTREE_CACHE_PASSWORD` should not contain comma `,` or colon `:` characters, otherwise the connection to the cache server may fail. The value specified for `INVENTREE_CACHE_PASSWORD` should not contain comma `,` or colon `:` characters, otherwise the connection to the cache server may fail.
@@ -339,17 +332,16 @@ To enable [email functionality](../settings/email.md), email settings must be co
The following email settings are available: The following email settings are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_EMAIL_BACKEND") }} Email backend module |
| INVENTREE_EMAIL_BACKEND | email.backend | Email backend module | django.core.mail.backends.smtp.EmailBackend | {{ configsetting("INVENTREE_EMAIL_HOST") }} Email server host |
| INVENTREE_EMAIL_HOST | email.host | Email server host | *Not specified* | {{ configsetting("INVENTREE_EMAIL_PORT") }} Email server port |
| INVENTREE_EMAIL_PORT | email.port | Email server port | 25 | {{ configsetting("INVENTREE_EMAIL_USERNAME") }} Email account username |
| INVENTREE_EMAIL_USERNAME | email.username | Email account username | *Not specified* | {{ configsetting("INVENTREE_EMAIL_PASSWORD") }} Email account password |
| INVENTREE_EMAIL_PASSWORD | email.password | Email account password | *Not specified* | {{ configsetting("INVENTREE_EMAIL_TLS") }} Enable STARTTLS support (commonly port 567) |
| INVENTREE_EMAIL_TLS | email.tls | Enable STARTTLS support (commonly port 567) | False | {{ configsetting("INVENTREE_EMAIL_SSL") }} Enable legacy SSL/TLS support (commonly port 465) |
| INVENTREE_EMAIL_SSL | email.ssl | Enable legacy SSL/TLS support (commonly port 465) | False | {{ configsetting("INVENTREE_EMAIL_SENDER") }} Sending email address |
| INVENTREE_EMAIL_SENDER | email.sender | Sending email address | *Not specified* | {{ configsetting("INVENTREE_EMAIL_PREFIX") }} Prefix for subject text |
| INVENTREE_EMAIL_PREFIX | email.prefix | Prefix for subject text | [InvenTree] |
### Email Backend ### Email Backend
@@ -366,11 +358,10 @@ The "sender" email address is the address from which InvenTree emails are sent (
InvenTree requires some external directories for storing files: InvenTree requires some external directories for storing files:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_STATIC_ROOT") }} [Static files](./processes.md#static-files) directory |
| INVENTREE_STATIC_ROOT | static_root | [Static files](./processes.md#static-files) directory | *Not specified* | {{ configsetting("INVENTREE_MEDIA_ROOT") }} [Media files](./processes.md#media-files) directory |
| INVENTREE_MEDIA_ROOT | media_root | [Media files](./processes.md#media-files) directory | *Not specified* | {{ configsetting("INVENTREE_BACKUP_DIR") }} Directory for backup files |
| INVENTREE_BACKUP_DIR | backup_dir | Backup files directory | *Not specified* |
!!! tip "Serving Files" !!! tip "Serving Files"
Read the [proxy server documentation](./processes.md#proxy-server) for more information on hosting *static* and *media* files Read the [proxy server documentation](./processes.md#proxy-server) for more information on hosting *static* and *media* files
@@ -404,43 +395,41 @@ Alternatively this location can be specified with the `INVENTREE_BACKUP_DIR` env
It is also possible to use alternative storage backends for static and media files, at the moment there is direct provide direct support bundled for S3 and SFTP. Google cloud storage and Azure blob storage would also be supported by the [used library](https://django-storages.readthedocs.io), but require additional packages to be installed. It is also possible to use alternative storage backends for static and media files, at the moment there is direct provide direct support bundled for S3 and SFTP. Google cloud storage and Azure blob storage would also be supported by the [used library](https://django-storages.readthedocs.io), but require additional packages to be installed.
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_STORAGE_TARGET") }} Storage target to use for static and media files, valid options: local, s3, sftp |
| INVENTREE_STORAGE_TARGET | storage.target | Storage target to use for static and media files, valid options: local, s3, sftp | local |
#### S3 #### S3
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | | `INVENTREE_S3_ACCESS_KEY` | storage.s3.access_key | *Not specified* | Access key |
| INVENTREE_S3_ACCESS_KEY | storage.s3.access_key | Access key | *Not specified* | | `INVENTREE_S3_SECRET_KEY` | storage.s3.secret_key | *Not specified* | Secret key |
| INVENTREE_S3_SECRET_KEY | storage.s3.secret_key | Secret key | *Not specified* | | `INVENTREE_S3_BUCKET_NAME` | storage.s3.bucket_name | *Not specified* | Bucket name, required by most providers |
| INVENTREE_S3_BUCKET_NAME | storage.s3.bucket_name | Bucket name, required by most providers | *Not specified* | | `INVENTREE_S3_REGION_NAME` | storage.s3.region_name | *Not specified* | S3 region name |
| INVENTREE_S3_REGION_NAME | storage.s3.region_name | S3 region name | *Not specified* | | `INVENTREE_S3_ENDPOINT_URL` | storage.s3.endpoint_url | *Not specified* | Custom S3 endpoint URL, defaults to AWS endpoints if not set |
| INVENTREE_S3_ENDPOINT_URL | storage.s3.endpoint_url | Custom S3 endpoint URL, defaults to AWS endpoints if not set | *Not specified* | | `INVENTREE_S3_LOCATION` | storage.s3.location | inventree-server | Sub-Location that should be used |
| INVENTREE_S3_LOCATION | storage.s3.location | Sub-Location that should be used | inventree-server | | `INVENTREE_S3_DEFAULT_ACL` | storage.s3.default_acl | *Not specified* | Default ACL for uploaded files, defaults to provider default if not set |
| INVENTREE_S3_DEFAULT_ACL | storage.s3.default_acl | Default ACL for uploaded files, defaults to provider default if not set | *Not specified* | | `INVENTREE_S3_VERIFY_SSL` | storage.s3.verify_ssl | True | Verify SSL certificate for S3 endpoint |
| INVENTREE_S3_VERIFY_SSL | storage.s3.verify_ssl | Verify SSL certificate for S3 endpoint | True | | `INVENTREE_S3_OVERWRITE` | storage.s3.overwrite | False | Overwrite existing files in S3 bucket |
| INVENTREE_S3_OVERWRITE | storage.s3.overwrite | Overwrite existing files in S3 bucket | False | | `INVENTREE_S3_VIRTUAL` | storage.s3.virtual | False | Use virtual addressing style - by default False -> `path` style, `virtual` style if True |
| INVENTREE_S3_VIRTUAL | storage.s3.virtual | Use virtual addressing style - by default False -> `path` style, `virtual` style if True | False |
#### SFTP #### SFTP
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | | `INVENTREE_SFTP_HOST` | storage.sftp.host | *Not specified* | SFTP host |
| INVENTREE_SFTP_HOST | storage.sftp.host | SFTP host | *Not specified* | | `INVENTREE_SFTP_PARAMS` | storage.sftp.params | *Not specified* | SFTP connection parameters, see https://docs.paramiko.org/en/latest/api/client.html#paramiko.client.SSHClient.connect; e.g. `{'port': 22, 'user': 'usr', 'password': 'pwd'}` |
| INVENTREE_SFTP_PARAMS | storage.sftp.params | SFTP connection parameters, see https://docs.paramiko.org/en/latest/api/client.html#paramiko.client.SSHClient.connect; e.g. `{'port': 22, 'user': 'usr', 'password': 'pwd'}` | *Not specified* | | `INVENTREE_SFTP_UID` | storage.sftp.uid | *Not specified* | SFTP user ID - not required |
| INVENTREE_SFTP_UID | storage.sftp.uid | SFTP user ID - not required | *Not specified* | | `INVENTREE_SFTP_GID` | storage.sftp.gid | *Not specified* | SFTP group ID - not required |
| INVENTREE_SFTP_GID | storage.sftp.gid | SFTP group ID - not required | *Not specified* | | `INVENTREE_SFTP_LOCATION` | storage.sftp.location | inventree-server | Sub-Location that should be used |
| INVENTREE_SFTP_LOCATION | storage.sftp.location | Sub-Location that should be used | inventree-server |
## Authentication ## Authentication
InvenTree provides allowance for additional sign-in options. The following options are not enabled by default, and care must be taken by the system administrator when configuring these settings. InvenTree provides allowance for additional sign-in options. The following options are not enabled by default, and care must be taken by the system administrator when configuring these settings.
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_MFA_ENABLED") }} Enable multi-factor authentication support for the InvenTree server |
| INVENTREE_MFA_ENABLED | mfa_enabled | Enable or disable multi-factor authentication support for the InvenTree server | True | {{ configsetting("INVENTREE_MFA_SUPPORTED_TYPES") }} List of supported multi-factor authentication types |
| INVENTREE_MFA_SUPPORTED_TYPES | mfa_supported_types | List of supported multi-factor authentication types | recovery_codes,totp,webauthn | {{ configsetting("INVENTREE_USE_JWT") }} Enable support for JSON Web Tokens (JWT) for authentication |
### Single Sign On ### Single Sign On
@@ -455,11 +444,10 @@ There are multiple configuration parameters which must be specified (either in y
The login-experience can be altered with the following settings: The login-experience can be altered with the following settings:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_LOGIN_CONFIRM_DAYS") }} Duration for which confirmation links are valid |
| INVENTREE_LOGIN_CONFIRM_DAYS | login_confirm_days | Duration for which confirmation links are valid | 3 | {{ configsetting("INVENTREE_LOGIN_ATTEMPTS") }} Count of allowed login attempts before blocking user |
| INVENTREE_LOGIN_ATTEMPTS | login_attempts | Count of allowed login attempts before blocking user | 5 | {{ configsetting("INVENTREE_LOGIN_DEFAULT_HTTP_PROTOCOL", default="Uses the protocol specified in `INVENTREE_SITE_URL`, or defaults to *http*") }} Default protocol to use for login callbacks (e.g. using [SSO](#single-sign-on)) |
| INVENTREE_LOGIN_DEFAULT_HTTP_PROTOCOL | login_default_protocol | Default protocol to use for login callbacks (e.g. using [SSO](#single-sign-on)) | Uses the protocol specified in `INVENTREE_SITE_URL`, or defaults to *http* |
!!! tip "Default Protocol" !!! tip "Default Protocol"
If you have specified `INVENTREE_SITE_URL`, the default protocol will be used from that setting. Otherwise, the default protocol will be *http*. If you have specified `INVENTREE_SITE_URL`, the default protocol will be used from that setting. Otherwise, the default protocol will be *http*.
@@ -472,22 +460,20 @@ Custom authentication backends can be used by specifying them here. These can fo
The following options are available for configuring the InvenTree [background worker process](./processes.md#background-worker): The following options are available for configuring the InvenTree [background worker process](./processes.md#background-worker):
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_BACKGROUND_WORKERS") }} Number of background worker processes |
| INVENTREE_BACKGROUND_WORKERS | background.workers | Number of background worker processes | 1 | {{ configsetting("INVENTREE_BACKGROUND_TIMEOUT") }} Timeout for background worker tasks (seconds) |
| INVENTREE_BACKGROUND_TIMEOUT | background.timeout | Timeout for background worker tasks (seconds) | 90 | {{ configsetting("INVENTREE_BACKGROUND_RETRY") }} Time to wait before retrying a background task (seconds) |
| INVENTREE_BACKGROUND_RETRY | background.retry | Time to wait before retrying a background task (seconds) | 300 | {{ configsetting("INVENTREE_BACKGROUND_MAX_ATTEMPTS") }} Maximum number of attempts for a background task |
| INVENTREE_BACKGROUND_MAX_ATTEMPTS | background.max_attempts | Maximum number of attempts for a background task | 5 |
## Sentry Integration ## Sentry Integration
The InvenTree server can be integrated with the [sentry.io](https://sentry.io) monitoring service, for error logging and performance tracking. The InvenTree server can be integrated with the [sentry.io](https://sentry.io) monitoring service, for error logging and performance tracking.
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_SENTRY_ENABLED") }} Enable sentry.io integration |
| INVENTREE_SENTRY_ENABLED | sentry_enabled | Enable sentry.io integration | False | {{ configsetting("INVENTREE_SENTRY_DSN", default="Defaults to InvenTree developer key") }} Sentry DSN (data source name) key |
| INVENTREE_SENTRY_DSN | sentry_dsn | Sentry DSN (data source name) key | *Defaults to InvenTree developer key* | {{ configsetting("INVENTREE_SENTRY_SAMPLE_RATE") }} How often to send data samples (seconds) |
| INVENTREE_SENTRY_SAMPLE_RATE | sentry_sample_rate | How often to send data samples | 0.1 |
!!! info "Default DSN" !!! info "Default DSN"
If enabled with the default DSN, server errors will be logged to a sentry.io account monitored by the InvenTree developers. If enabled with the default DSN, server errors will be logged to a sentry.io account monitored by the InvenTree developers.
@@ -496,13 +482,11 @@ The InvenTree server can be integrated with the [sentry.io](https://sentry.io) m
The logo and custom messages can be changed/set: The logo and custom messages can be changed/set:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_CUSTOM_LOGO") }} Path to custom logo in the static files directory |
| INVENTREE_CUSTOM_LOGO | customize.logo | Path to custom logo in the static files directory | *Not specified* | {{ configsetting("INVENTREE_CUSTOM_SPLASH") }} Path to custom splash screen in the static files directory |
| INVENTREE_CUSTOM_SPLASH | customize.splash | Path to custom splash screen in the static files directory | *Not specified* | {{ configsetting("INVENTREE_SITE_HEADER") }} Custom header text for the django admin page |
| INVENTREE_CUSTOMIZE | customize.site_header | Custom site header in the Django admin | InvenTree Admin | {{ configsetting("INVENTREE_CUSTOMIZE") }} JSON object containing custom messages for the login page, navbar, and Django admin site |
| INVENTREE_CUSTOMIZE | customize.login_message | Custom message for login page | *Not specified* |
| INVENTREE_CUSTOMIZE | customize.navbar_message | Custom message for navbar | *Not specified* |
The INVENTREE_CUSTOMIZE environment variable must contain a json object with the keys from the table above and The INVENTREE_CUSTOMIZE environment variable must contain a json object with the keys from the table above and
the wanted values. Example: the wanted values. Example:
@@ -547,15 +531,15 @@ INVENTREE_FRONTEND_SETTINGS='{"mobile_mode": "allow-ignore"}'
The following [plugin](../plugins/index.md) configuration options are available: The following [plugin](../plugins/index.md) configuration options are available:
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_PLUGINS_ENABLED") }} Enable plugin support |
| INVENTREE_PLUGINS_ENABLED | plugins_enabled | Enable plugin support | False | {{ configsetting("INVENTREE_PLUGIN_NOINSTALL") }} Disable Plugin installation via API |
| INVENTREE_PLUGIN_NOINSTALL | plugin_noinstall | Disable Plugin installation via API - only use plugins.txt file | False | {{ configsetting("INVENTREE_PLUGIN_FILE") }} Location of plugin installation file |
| INVENTREE_PLUGIN_FILE | plugins_plugin_file | Location of plugin installation file | *Not specified* | | `INVENTREE_PLUGIN_DIR` | `plugin_dir` | *Not specified* | Location of external plugin directory |
| INVENTREE_PLUGIN_DIR | plugins_plugin_dir | Location of external plugin directory | *Not specified* | {{ configsetting("INVENTREE_PLUGIN_RETRY") }} Number of tries to attempt loading a plugin before giving up |
| INVENTREE_PLUGINS_MANDATORY | plugins_mandatory | List of [plugins which are considered mandatory](../plugins/index.md#mandatory-third-party-plugins) | *Not specified* | {{ configsetting("INVENTREE_PLUGINS_MANDATORY") }} List of [plugins which are considered mandatory](../plugins/index.md#mandatory-third-party-plugins) |
| INVENTREE_PLUGIN_DEV_SLUG | plugin_dev.slug | Specify plugin to run in [development mode](../plugins/creator.md#backend-configuration) | *Not specified* | {{ configsetting("INVENTREE_PLUGIN_DEV_SLUG") }} Specify plugin to run in [development mode](../plugins/creator.md#backend-configuration) |
| INVENTREE_PLUGIN_DEV_HOST | plugin_dev.host | Specify host for development mode plugin | http://localhost:5174 | {{ configsetting("INVENTREE_PLUGIN_DEV_HOST") }} Specify host for development mode plugin |
## Override Global Settings ## Override Global Settings
@@ -563,6 +547,12 @@ If required, [global settings values](../settings/global.md#override-global-sett
To override global settings, provide a "dictionary" of settings overrides in the configuration file, or via an environment variable. To override global settings, provide a "dictionary" of settings overrides in the configuration file, or via an environment variable.
| Environment Variable | Configuration File | Description | Default | {{ configtable() }}
| --- | --- | --- | --- | {{ configsetting("INVENTREE_GLOBAL_SETTINGS") }} JSON object containing global settings overrides |
| INVENTREE_GLOBAL_SETTINGS | global_settings | JSON object containing global settings overrides | *Not specified* |
## Other Settings
Other available settings, not categorized above, are detailed in the table below:
{{ configtable() }}
{{ configsetting("INVENTREE_EXTRA_URL_SCHEMES") }} Allow additional URL schemes for URL validation |
+3 -9
View File
@@ -87,13 +87,7 @@ If you are creating the initial database, you need to create an admin (superuser
docker compose run inventree-server invoke superuser docker compose run inventree-server invoke superuser
``` ```
Alternatively, admin account details can be specified in the `.env` file, removing the need for this manual step: Alternatively, admin account details can be specified using environment variables, or in the `.env` file, removing the need for this manual step. Refer to the [configuration documentation](./config.md#administrator-account) for more information.
| Variable | Description |
| --- | --- |
| INVENTREE_ADMIN_USER | Admin account username |
| INVENTREE_ADMIN_PASSWORD | Admin account password |
| INVENTREE_ADMIN_EMAIL | Admin account email address |
!!! warning "Scrub Account Data" !!! warning "Scrub Account Data"
Ensure that the admin account credentials are removed from the `.env` file after the first run, for security. Ensure that the admin account credentials are removed from the `.env` file after the first run, for security.
@@ -242,8 +236,8 @@ This can be adjusted using the following environment variables:
| Environment Variable | Default | | Environment Variable | Default |
| --- | --- | | --- | --- |
| INVENTREE_WEB_ADDR | 0.0.0.0 | | `INVENTREE_WEB_ADDR` | 0.0.0.0 |
| INVENTREE_WEB_PORT | 8000 | | `INVENTREE_WEB_PORT` | 8000 |
These variables are combined in the [Dockerfile]({{ sourcefile("contrib/container/Dockerfile") }}) to build the bind string passed to the InvenTree server on startup. These variables are combined in the [Dockerfile]({{ sourcefile("contrib/container/Dockerfile") }}) to build the bind string passed to the InvenTree server on startup.
+30
View File
@@ -34,6 +34,7 @@ for key in [
print(f' - {key}: {val}') print(f' - {key}: {val}')
# Cached settings dict values # Cached settings dict values
global CONFIG_SETTINGS
global GLOBAL_SETTINGS global GLOBAL_SETTINGS
global USER_SETTINGS global USER_SETTINGS
global TAGS global TAGS
@@ -64,6 +65,7 @@ with open(settings_file, encoding='utf-8') as sf:
GLOBAL_SETTINGS = settings['global'] GLOBAL_SETTINGS = settings['global']
USER_SETTINGS = settings['user'] USER_SETTINGS = settings['user']
CONFIG_SETTINGS = settings['config']
# Tags # Tags
with open(gen_base.joinpath('inventree_tags.yml'), encoding='utf-8') as f: with open(gen_base.joinpath('inventree_tags.yml'), encoding='utf-8') as f:
@@ -377,6 +379,34 @@ def define_env(env):
return rendersetting(key, setting, short=short) return rendersetting(key, setting, short=short)
@env.macro
def configtable():
"""Generate a header for the configuration settings table."""
return '| Environment Variable | Configuration File | Default | Description |\n| --- | --- | --- | --- |'
@env.macro
def configsetting(key: str, default: Optional[str] = None):
"""Extract information on a particular configuration setting.
Arguments:
key: The name of the configuration setting to extract information for.
default: An optional default value to override the setting's default display value.
"""
global CONFIG_SETTINGS
setting = CONFIG_SETTINGS[key]
observe_setting(key, 'config')
cfg_key = setting.get('config_key', None)
cfg_key = f'`{cfg_key}`' if cfg_key else '-'
default = default or setting.get('default_value', None)
if default is None:
default = '*Not Specified*'
return f'| <span title="{key}" style="white-space: nowrap;"><code>{key}</code></span> | {cfg_key} | {default} |'
@env.macro @env.macro
def tags_and_filters(): def tags_and_filters():
"""Return a list of all tags and filters.""" """Return a list of all tags and filters."""
+1 -1
View File
@@ -297,7 +297,7 @@ class InfoView(APIView):
'instance': InvenTree.version.inventreeInstanceName(), 'instance': InvenTree.version.inventreeInstanceName(),
'apiVersion': InvenTree.version.inventreeApiVersion(), 'apiVersion': InvenTree.version.inventreeApiVersion(),
'worker_running': is_worker_running(), 'worker_running': is_worker_running(),
'worker_count': settings.BACKGROUND_WORKER_COUNT, 'worker_count': settings.Q_CLUSTER['workers'],
'worker_pending_tasks': self.worker_pending_tasks(), 'worker_pending_tasks': self.worker_pending_tasks(),
'plugins_enabled': settings.PLUGINS_ENABLED, 'plugins_enabled': settings.PLUGINS_ENABLED,
'plugins_install_disabled': settings.PLUGINS_INSTALL_DISABLED, 'plugins_install_disabled': settings.PLUGINS_INSTALL_DISABLED,
+39 -39
View File
@@ -94,51 +94,51 @@ def get_cache_config(global_cache: bool) -> dict:
Returns: Returns:
A dictionary containing the cache configuration options. A dictionary containing the cache configuration options.
""" """
if global_cache: # Build Redis URL with optional password
# Build Redis URL with optional password password = cache_password()
password = cache_password() user = cache_user() or ''
user = cache_user() or '' host = cache_host()
port = cache_port()
db = cache_db()
if password: if password:
redis_url = ( redis_url = f'redis://{user}:{password}@{host}:{port}/{db}'
f'redis://{user}:{password}@{cache_host()}:{cache_port()}/{cache_db()}' else:
) redis_url = f'redis://{host}:{port}/{db}'
else:
redis_url = f'redis://{cache_host()}:{cache_port()}/{cache_db()}'
keepalive_options = { keepalive_options = {
'TCP_KEEPCNT': cache_setting('keepalive_count', 5, typecast=int), 'TCP_KEEPCNT': cache_setting('keepalive_count', 5, typecast=int),
'TCP_KEEPIDLE': cache_setting('keepalive_idle', 1, typecast=int), 'TCP_KEEPIDLE': cache_setting('keepalive_idle', 1, typecast=int),
'TCP_KEEPINTVL': cache_setting('keepalive_interval', 1, typecast=int), 'TCP_KEEPINTVL': cache_setting('keepalive_interval', 1, typecast=int),
'TCP_USER_TIMEOUT': cache_setting('user_timeout', 1000, typecast=int), 'TCP_USER_TIMEOUT': cache_setting('user_timeout', 1000, typecast=int),
} }
return { global_cache_config = {
'BACKEND': 'django_redis.cache.RedisCache', 'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': redis_url, 'LOCATION': redis_url,
'OPTIONS': { 'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient', 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'SOCKET_CONNECT_TIMEOUT': cache_setting( 'SOCKET_CONNECT_TIMEOUT': cache_setting('connect_timeout', 5, typecast=int),
'connect_timeout', 5, typecast=int 'SOCKET_TIMEOUT': cache_setting('timeout', 3, typecast=int),
), 'CONNECTION_POOL_KWARGS': {
'SOCKET_TIMEOUT': cache_setting('timeout', 3, typecast=int), 'socket_keepalive': cache_setting('tcp_keepalive', True, typecast=bool),
'CONNECTION_POOL_KWARGS': { 'socket_keepalive_options': {
'socket_keepalive': cache_setting( # Only include options which are available on this platform
'tcp_keepalive', True, typecast=bool # e.g. MacOS does not have TCP_KEEPIDLE and TCP_USER_TIMEOUT
), getattr(socket, key): value
'socket_keepalive_options': { for key, value in keepalive_options.items()
# Only include options which are available on this platform if hasattr(socket, key)
# e.g. MacOS does not have TCP_KEEPIDLE and TCP_USER_TIMEOUT
getattr(socket, key): value
for key, value in keepalive_options.items()
if hasattr(socket, key)
},
}, },
}, },
} },
}
# Default: Use django local memory cache if global_cache:
return {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'} # Only return the global cache configuration if the global cache is enabled
return global_cache_config
else:
# Default: Use django local memory cache
return {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}
def create_session_cache(request) -> None: def create_session_cache(request) -> None:
+8 -4
View File
@@ -270,10 +270,13 @@ def get_setting(env_var=None, config_key=None, default_value=None, typecast=None
def set_metadata(source: str): def set_metadata(source: str):
"""Set lookup metadata for the setting.""" """Set lookup metadata for the setting."""
global CONFIG_LOOKUPS
key = env_var or config_key key = env_var or config_key
CONFIG_LOOKUPS[key] = { CONFIG_LOOKUPS[key] = {
'env_var': env_var, 'env_var': env_var,
'config_key': config_key, 'config_key': config_key,
'default_value': default_value,
'source': source, 'source': source,
'accessed': datetime.datetime.now(), 'accessed': datetime.datetime.now(),
} }
@@ -544,13 +547,14 @@ def get_frontend_settings(debug=True):
'INVENTREE_FRONTEND_SETTINGS', 'frontend_settings', {}, typecast=dict 'INVENTREE_FRONTEND_SETTINGS', 'frontend_settings', {}, typecast=dict
) )
base_url = get_setting(
'INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'web', typecast=str
)
# Set the base URL for the user interface # Set the base URL for the user interface
# This is the UI path e.g. '/web/' # This is the UI path e.g. '/web/'
if 'base_url' not in frontend_settings: if 'base_url' not in frontend_settings:
frontend_settings['base_url'] = ( frontend_settings['base_url'] = base_url
get_setting('INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'web')
or 'web'
)
# If provided, specify the API host # If provided, specify the API host
api_host = frontend_settings.get('api_host', None) or get_setting( api_host = frontend_settings.get('api_host', None) or get_setting(
@@ -24,8 +24,9 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs): def handle(self, *args, **kwargs):
"""Export settings information to a JSON file.""" """Export settings information to a JSON file."""
from common.models import InvenTreeSetting, InvenTreeUserSetting from common.models import InvenTreeSetting, InvenTreeUserSetting
from InvenTree.config import CONFIG_LOOKUPS
settings = {'global': {}, 'user': {}} settings = {'global': {}, 'user': {}, 'config': {}}
# Global settings # Global settings
for key, setting in InvenTreeSetting.SETTINGS.items(): for key, setting in InvenTreeSetting.SETTINGS.items():
@@ -45,6 +46,18 @@ class Command(BaseCommand):
'units': str(setting.get('units', '')), 'units': str(setting.get('units', '')),
} }
# Configuration settings
for key, config in CONFIG_LOOKUPS.items():
default_value = config.get('default_value', None)
if default_value is not None:
default_value = str(default_value)
settings['config'][key] = {
'env_var': str(config.get('env_var', '')),
'config_key': str(config.get('config_key', '')),
'default_value': default_value,
}
filename = kwargs.get('filename', 'inventree_settings.json') filename = kwargs.get('filename', 'inventree_settings.json')
with open(filename, 'w', encoding='utf-8') as f: with open(filename, 'w', encoding='utf-8') as f:
@@ -1,5 +1,7 @@
"""Configuration settings specific to a particular database backend.""" """Configuration settings specific to a particular database backend."""
from pathlib import Path
import structlog import structlog
from InvenTree.config import get_boolean_setting, get_setting from InvenTree.config import get_boolean_setting, get_setting
@@ -7,6 +9,62 @@ from InvenTree.config import get_boolean_setting, get_setting
logger = structlog.get_logger('inventree') logger = structlog.get_logger('inventree')
def get_db_backend():
"""Return the database backend configuration."""
db_config = {
'ENGINE': get_setting('INVENTREE_DB_ENGINE', 'database.engine', None),
'NAME': get_setting('INVENTREE_DB_NAME', 'database.name', None),
'USER': get_setting('INVENTREE_DB_USER', 'database.user', None),
'PASSWORD': get_setting('INVENTREE_DB_PASSWORD', 'database.password', None),
'HOST': get_setting('INVENTREE_DB_HOST', 'database.host', None),
'PORT': get_setting('INVENTREE_DB_PORT', 'database.port', 5432, typecast=int),
'OPTIONS': get_setting(
'INVENTREE_DB_OPTIONS', 'database.options', {}, typecast=dict
)
or {},
}
# Check for required keys
required_keys = ['ENGINE', 'NAME']
for key in required_keys:
if not db_config[key]:
raise ValueError(
f'Missing required database configuration key: INVENTREE_DB_{key}'
)
DB_ENGINE = db_config['ENGINE'].lower()
# Correct common misspelling
if DB_ENGINE == 'sqlite':
DB_ENGINE = 'sqlite3' # pragma: no cover
if DB_ENGINE in ['sqlite3', 'postgresql', 'mysql']:
# Prepend the required python module string
DB_ENGINE = f'django.db.backends.{DB_ENGINE}'
db_config['ENGINE'] = DB_ENGINE
if 'sqlite' in DB_ENGINE:
db_name = str(Path(db_config['NAME']).resolve())
db_config['NAME'] = db_name
logger.info('DB_ENGINE: %s', DB_ENGINE)
logger.info('DB_NAME: %s', db_config['NAME'])
logger.info('DB_HOST: %s', db_config.get('HOST', "''"))
# Set testing options for the database
db_config['TEST'] = {'CHARSET': 'utf8'}
# Set collation option for mysql test database
if 'mysql' in DB_ENGINE:
db_config['TEST']['COLLATION'] = 'utf8_general_ci' # pragma: no cover
# Specify database specific configuration
set_db_options(DB_ENGINE, db_config['OPTIONS'])
return db_config
def set_db_options(engine: str, db_options: dict): def set_db_options(engine: str, db_options: dict):
"""Update database options based on the specified database backend. """Update database options based on the specified database backend.
@@ -0,0 +1,139 @@
"""Configuration of LDAP support for InvenTree."""
from InvenTree.config import get_boolean_setting, get_setting
def get_ldap_config(debug: bool = False) -> dict:
"""Return a dictionary of LDAP configuration settings.
The returned settings will be updated into the globals() object,
and will be used to configure the LDAP authentication backend.
"""
import django_auth_ldap.config # type: ignore[unresolved-import]
import ldap # type: ignore[unresolved-import]
# get global options from dict and use ldap.OPT_* as keys and values
global_options_dict = get_setting(
'INVENTREE_LDAP_GLOBAL_OPTIONS',
'ldap.global_options',
default_value=None,
typecast=dict,
)
global_options = {}
for k, v in global_options_dict.items():
# keys are always ldap.OPT_* constants
k_attr = getattr(ldap, k, None)
if not k.startswith('OPT_') or k_attr is None:
print(f"[LDAP] ldap.global_options, key '{k}' not found, skipping...")
continue
# values can also be other strings, e.g. paths
v_attr = v
if v.startswith('OPT_'):
v_attr = getattr(ldap, v, None)
if v_attr is None:
print(f"[LDAP] ldap.global_options, value key '{v}' not found, skipping...")
continue
global_options[k_attr] = v_attr
if debug:
print('[LDAP] ldap.global_options =', global_options)
group_type_class = get_setting(
'INVENTREE_LDAP_GROUP_TYPE_CLASS',
'ldap.group_type_class',
'GroupOfUniqueNamesType',
str,
)
group_type_class_args = get_setting(
'INVENTREE_LDAP_GROUP_TYPE_CLASS_ARGS', 'ldap.group_type_class_args', [], list
)
group_type_class_kwargs = get_setting(
'INVENTREE_LDAP_GROUP_TYPE_CLASS_KWARGS',
'ldap.group_type_class_kwargs',
{'name_attr': 'cn'},
dict,
)
group_object_class = get_setting(
'INVENTREE_LDAP_GROUP_OBJECT_CLASS',
'ldap.group_object_class',
'groupOfUniqueNames',
str,
)
ldap_config = {
'AUTH_LDAP_GLOBAL_OPTIONS': global_options,
'AUTH_LDAP_SERVER_URI': get_setting(
'INVENTREE_LDAP_SERVER_URI', 'ldap.server_uri'
),
'AUTH_LDAP_START_TLS': get_boolean_setting(
'INVENTREE_LDAP_START_TLS', 'ldap.start_tls', False
),
'AUTH_LDAP_BIND_DN': get_setting('INVENTREE_LDAP_BIND_DN', 'ldap.bind_dn'),
'AUTH_LDAP_BIND_PASSWORD': get_setting(
'INVENTREE_LDAP_BIND_PASSWORD', 'ldap.bind_password'
),
'AUTH_LDAP_USER_SEARCH': django_auth_ldap.config.LDAPSearch(
get_setting('INVENTREE_LDAP_SEARCH_BASE_DN', 'ldap.search_base_dn'),
ldap.SCOPE_SUBTREE,
str(
get_setting(
'INVENTREE_LDAP_SEARCH_FILTER_STR',
'ldap.search_filter_str',
'(uid= %(user)s)',
)
),
),
'AUTH_LDAP_USER_DN_TEMPLATE': get_setting(
'INVENTREE_LDAP_USER_DN_TEMPLATE', 'ldap.user_dn_template'
),
'AUTH_LDAP_USER_ATTR_MAP': get_setting(
'INVENTREE_LDAP_USER_ATTR_MAP',
'ldap.user_attr_map',
{'first_name': 'givenName', 'last_name': 'sn', 'email': 'mail'},
dict,
),
'AUTH_LDAP_ALWAYS_UPDATE_USER': get_boolean_setting(
'INVENTREE_LDAP_ALWAYS_UPDATE_USER', 'ldap.always_update_user', True
),
'AUTH_LDAP_CACHE_TIMEOUT': get_setting(
'INVENTREE_LDAP_CACHE_TIMEOUT', 'ldap.cache_timeout', 3600, int
),
'AUTH_LDAP_MIRROR_GROUPS': get_boolean_setting(
'INVENTREE_LDAP_MIRROR_GROUPS', 'ldap.mirror_groups', False
),
'AUTH_LDAP_GROUP_OBJECT_CLASS': group_object_class,
'AUTH_LDAP_GROUP_SEARCH': django_auth_ldap.config.LDAPSearch(
get_setting('INVENTREE_LDAP_GROUP_SEARCH', 'ldap.group_search'),
ldap.SCOPE_SUBTREE,
f'(objectClass={group_object_class})',
),
'AUTH_LDAP_GROUP_TYPE_CLASS': group_type_class,
'AUTH_LDAP_GROUP_TYPE_CLASS_ARGS': [*group_type_class_args],
'AUTH_LDAP_GROUP_TYPE_CLASS_KWARGS': {**group_type_class_kwargs},
'AUTH_LDAP_GROUP_TYPE': getattr(django_auth_ldap.config, group_type_class)(
*group_type_class_args, **group_type_class_kwargs
),
'AUTH_LDAP_REQUIRE_GROUP': get_setting(
'INVENTREE_LDAP_REQUIRE_GROUP', 'ldap.require_group'
),
'AUTH_LDAP_DENY_GROUP': get_setting(
'INVENTREE_LDAP_DENY_GROUP', 'ldap.deny_group'
),
'AUTH_LDAP_USER_FLAGS_BY_GROUP': get_setting(
'INVENTREE_LDAP_USER_FLAGS_BY_GROUP',
'ldap.user_flags_by_group',
default_value=None,
typecast=dict,
),
'AUTH_LDAP_FIND_GROUP_PERMS': True,
}
return ldap_config
@@ -0,0 +1,61 @@
"""Start-up configuration options for InvenTree tracing integrations."""
import structlog
from InvenTree.config import get_boolean_setting, get_setting
from InvenTree.tracing import setup_instruments, setup_tracing
logger = structlog.get_logger('inventree')
def configure_tracing(db_engine: str, enabled: bool, tags: dict) -> dict:
"""Configure tracing integrations for InvenTree.
Arguments:
db_engine: The database engine being used
enabled: Whether tracing is enabled
tags: A dictionary of tags to be included in tracing spans, with keys prefixed by
"""
endpoint = get_setting('INVENTREE_TRACING_ENDPOINT', 'tracing.endpoint', None)
headers = (
get_setting('INVENTREE_TRACING_HEADERS', 'tracing.headers', None, typecast=dict)
or {}
)
if enabled and endpoint:
logger.info('Tracing enabled with endpoint: %s', endpoint)
TRACING_DETAILS = {
'endpoint': endpoint,
'headers': headers,
'resources_input': {
**{'inventree.env.' + k: v for k, v in tags.items()},
**get_setting(
'INVENTREE_TRACING_RESOURCES',
'tracing.resources',
default_value=None,
typecast=dict,
),
},
'console': get_boolean_setting(
'INVENTREE_TRACING_CONSOLE', 'tracing.console', False
),
'auth': get_setting(
'INVENTREE_TRACING_AUTH', 'tracing.auth', default_value=None, typecast=dict
),
'is_http': get_setting('INVENTREE_TRACING_IS_HTTP', 'tracing.is_http', True),
'append_http': get_boolean_setting(
'INVENTREE_TRACING_APPEND_HTTP', 'tracing.append_http', True
),
}
if not enabled:
return None
if endpoint:
setup_tracing(**TRACING_DETAILS)
setup_instruments(db_engine)
else:
logger.warning('OpenTelemetry tracing not enabled because endpoint is not set')
return TRACING_DETAILS
@@ -0,0 +1,88 @@
"""Configuration settings for the InvenTree background worker process."""
import sys
from InvenTree.config import get_setting
def get_worker_config(
db_engine: str,
global_cache: bool = False,
sentry_dsn: str = '',
debug: bool = False,
) -> dict:
"""Return a dictionary of configuration settings for the background worker.
Arguments:
db_engine: The database engine being used (e.g. 'sqlite', 'postgresql', 'mysql')
global_cache: Whether a global redis cache is enabled
sentry_dsn: The DSN for sentry.io integration (if enabled)
debug: Whether the application is running in debug mode
Ref: https://django-q2.readthedocs.io/en/master/configure.html
"""
BACKGROUND_WORKER_TIMEOUT = int(
get_setting('INVENTREE_BACKGROUND_TIMEOUT', 'background.timeout', 90)
)
# Set the retry time for background workers to be slightly longer than the worker timeout, to ensure that workers have time to timeout before being retried
BACKGROUND_WORKER_RETRY = max(
int(get_setting('INVENTREE_BACKGROUND_RETRY', 'background.retry', 300)),
BACKGROUND_WORKER_TIMEOUT + 120,
)
BACKGROUND_WORKER_ATTEMPTS = int(
get_setting('INVENTREE_BACKGROUND_MAX_ATTEMPTS', 'background.max_attempts', 5)
)
# Prevent running multiple background workers if global cache is disabled
# This is to prevent scheduling conflicts due to the lack of a shared cache
BACKGROUND_WORKER_COUNT = int(
get_setting('INVENTREE_BACKGROUND_WORKERS', 'background.workers', 4)
)
# If global cache is disabled, we cannot run multiple background workers
if not global_cache:
BACKGROUND_WORKER_COUNT = 1
# If running with SQLite, limit background worker threads to 1 to prevent database locking issues
if 'sqlite' in db_engine:
BACKGROUND_WORKER_COUNT = 1
# Check if '--sync' was passed in the command line
if '--sync' in sys.argv and '--noreload' in sys.argv and debug:
SYNC_TASKS = True
else:
SYNC_TASKS = False
# Clean up sys.argv so Django doesn't complain about an unknown argument
if SYNC_TASKS:
sys.argv.remove('--sync')
# django-q background worker configuration
config = {
'name': 'InvenTree',
'label': 'Background Tasks',
'workers': BACKGROUND_WORKER_COUNT,
'timeout': BACKGROUND_WORKER_TIMEOUT,
'retry': BACKGROUND_WORKER_RETRY,
'max_attempts': BACKGROUND_WORKER_ATTEMPTS,
'save_limit': 1000,
'queue_limit': 50,
'catch_up': False,
'bulk': 10,
'orm': 'default',
'cache': 'default',
'sync': SYNC_TASKS,
'poll': 1.5,
}
if global_cache:
# If using external redis cache, make the cache the broker for Django Q
config['django_redis'] = 'worker'
if sentry_dsn:
# If sentry is enabled, configure django-q to report errors to sentry
config['error_reporter'] = {'sentry': {'dsn': sentry_dsn}}
return config
+55 -347
View File
@@ -12,7 +12,6 @@ database setup in this file.
import logging import logging
import os import os
import sys import sys
from pathlib import Path
from typing import Optional from typing import Optional
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
@@ -33,7 +32,16 @@ from InvenTree.version import checkMinPythonVersion, inventreeCommitHash
from users.oauth2_scopes import oauth2_scopes from users.oauth2_scopes import oauth2_scopes
from . import config from . import config
from .setting import db_backend, locales, markdown, spectacular, storages from .setting import (
db_backend,
ldap,
locales,
markdown,
spectacular,
storages,
tracing,
worker,
)
try: try:
import django_stubs_ext import django_stubs_ext
@@ -81,6 +89,8 @@ DEBUG = get_boolean_setting('INVENTREE_DEBUG', 'debug', False)
# Internal flag to determine if we are running in docker mode # Internal flag to determine if we are running in docker mode
DOCKER = get_boolean_setting('INVENTREE_DOCKER', default_value=False) DOCKER = get_boolean_setting('INVENTREE_DOCKER', default_value=False)
AUTO_UPDATE = get_boolean_setting('INVENTREE_AUTO_UPDATE', 'auto_update', False)
# Configure logging settings # Configure logging settings
LOG_LEVEL = get_setting('INVENTREE_LOG_LEVEL', 'log_level', 'WARNING') LOG_LEVEL = get_setting('INVENTREE_LOG_LEVEL', 'log_level', 'WARNING')
JSON_LOG = get_boolean_setting('INVENTREE_JSON_LOG', 'json_log', False) JSON_LOG = get_boolean_setting('INVENTREE_JSON_LOG', 'json_log', False)
@@ -222,9 +232,7 @@ PLUGIN_TESTING_EVENTS_ASYNC = False # Flag if events are tested asynchronously
PLUGIN_TESTING_RELOAD = False # Flag if plugin reloading is in testing (check_reload) PLUGIN_TESTING_RELOAD = False # Flag if plugin reloading is in testing (check_reload)
# Plugin development settings # Plugin development settings
PLUGIN_DEV_SLUG = ( PLUGIN_DEV_SLUG = get_setting('INVENTREE_PLUGIN_DEV_SLUG', 'plugin_dev.slug')
get_setting('INVENTREE_PLUGIN_DEV_SLUG', 'plugin_dev.slug') if DEBUG else None
)
PLUGIN_DEV_HOST = get_setting( PLUGIN_DEV_HOST = get_setting(
'INVENTREE_PLUGIN_DEV_HOST', 'plugin_dev.host', 'http://localhost:5174' 'INVENTREE_PLUGIN_DEV_HOST', 'plugin_dev.host', 'http://localhost:5174'
@@ -386,8 +394,11 @@ MIDDLEWARE = CONFIG.get(
# In DEBUG mode, add support for django-silk # In DEBUG mode, add support for django-silk
# Ref: https://silk.readthedocs.io/en/latest/ # Ref: https://silk.readthedocs.io/en/latest/
DJANGO_SILK_ENABLED = DEBUG and get_boolean_setting( # pragma: no cover DJANGO_SILK_ENABLED = (
'INVENTREE_DEBUG_SILK', 'debug_silk', False get_boolean_setting( # pragma: no cover
'INVENTREE_DEBUG_SILK', 'debug_silk', False
)
and DEBUG
) )
if DJANGO_SILK_ENABLED: # pragma: no cover if DJANGO_SILK_ENABLED: # pragma: no cover
@@ -401,8 +412,11 @@ if DJANGO_SILK_ENABLED: # pragma: no cover
# In DEBUG mode, add support for django-querycount # In DEBUG mode, add support for django-querycount
# Ref: https://github.com/bradmontgomery/django-querycount # Ref: https://github.com/bradmontgomery/django-querycount
if DEBUG and get_boolean_setting( # pragma: no cover if (
'INVENTREE_DEBUG_QUERYCOUNT', 'debug_querycount', False get_boolean_setting( # pragma: no cover
'INVENTREE_DEBUG_QUERYCOUNT', 'debug_querycount', False
)
and DEBUG
): ):
MIDDLEWARE.append('querycount.middleware.QueryCountMiddleware') MIDDLEWARE.append('querycount.middleware.QueryCountMiddleware')
logger.debug('Running with debug_querycount middleware enabled') logger.debug('Running with debug_querycount middleware enabled')
@@ -437,14 +451,14 @@ AUTHENTICATION_BACKENDS = (
# LDAP support # LDAP support
LDAP_AUTH = get_boolean_setting('INVENTREE_LDAP_ENABLED', 'ldap.enabled', False) LDAP_AUTH = get_boolean_setting('INVENTREE_LDAP_ENABLED', 'ldap.enabled', False)
if LDAP_AUTH: # pragma: no cover LDAP_DEBUG = (
import django_auth_ldap.config # type: ignore[unresolved-import] get_boolean_setting('INVENTREE_LDAP_DEBUG', 'ldap.debug', False) and LDAP_AUTH
import ldap # type: ignore[unresolved-import] )
if LDAP_AUTH: # pragma: no cover
AUTHENTICATION_BACKENDS.append('django_auth_ldap.backend.LDAPBackend') AUTHENTICATION_BACKENDS.append('django_auth_ldap.backend.LDAPBackend')
# debug mode to troubleshoot configuration # debug mode to troubleshoot configuration
LDAP_DEBUG = get_boolean_setting('INVENTREE_LDAP_DEBUG', 'ldap.debug', False)
if LDAP_DEBUG: if LDAP_DEBUG:
if 'loggers' not in LOGGING: if 'loggers' not in LOGGING:
LOGGING['loggers'] = {} LOGGING['loggers'] = {}
@@ -453,113 +467,10 @@ if LDAP_AUTH: # pragma: no cover
'handlers': DEFAULT_LOG_HANDLER, 'handlers': DEFAULT_LOG_HANDLER,
} }
# get global options from dict and use ldap.OPT_* as keys and values # Determine LDAP settings
global_options_dict = get_setting( ldap_settings = ldap.get_ldap_config(debug=LDAP_DEBUG)
'INVENTREE_LDAP_GLOBAL_OPTIONS', globals().update(ldap_settings)
'ldap.global_options',
default_value=None,
typecast=dict,
)
global_options = {}
for k, v in global_options_dict.items():
# keys are always ldap.OPT_* constants
k_attr = getattr(ldap, k, None)
if not k.startswith('OPT_') or k_attr is None:
print(f"[LDAP] ldap.global_options, key '{k}' not found, skipping...")
continue
# values can also be other strings, e.g. paths
v_attr = v
if v.startswith('OPT_'):
v_attr = getattr(ldap, v, None)
if v_attr is None:
print(f"[LDAP] ldap.global_options, value key '{v}' not found, skipping...")
continue
global_options[k_attr] = v_attr
AUTH_LDAP_GLOBAL_OPTIONS = global_options
if LDAP_DEBUG:
print('[LDAP] ldap.global_options =', global_options)
AUTH_LDAP_SERVER_URI = get_setting('INVENTREE_LDAP_SERVER_URI', 'ldap.server_uri')
AUTH_LDAP_START_TLS = get_boolean_setting(
'INVENTREE_LDAP_START_TLS', 'ldap.start_tls', False
)
AUTH_LDAP_BIND_DN = get_setting('INVENTREE_LDAP_BIND_DN', 'ldap.bind_dn')
AUTH_LDAP_BIND_PASSWORD = get_setting(
'INVENTREE_LDAP_BIND_PASSWORD', 'ldap.bind_password'
)
AUTH_LDAP_USER_SEARCH = django_auth_ldap.config.LDAPSearch(
get_setting('INVENTREE_LDAP_SEARCH_BASE_DN', 'ldap.search_base_dn'),
ldap.SCOPE_SUBTREE,
str(
get_setting(
'INVENTREE_LDAP_SEARCH_FILTER_STR',
'ldap.search_filter_str',
'(uid= %(user)s)',
)
),
)
AUTH_LDAP_USER_DN_TEMPLATE = get_setting(
'INVENTREE_LDAP_USER_DN_TEMPLATE', 'ldap.user_dn_template'
)
AUTH_LDAP_USER_ATTR_MAP = get_setting(
'INVENTREE_LDAP_USER_ATTR_MAP',
'ldap.user_attr_map',
{'first_name': 'givenName', 'last_name': 'sn', 'email': 'mail'},
dict,
)
AUTH_LDAP_ALWAYS_UPDATE_USER = get_boolean_setting(
'INVENTREE_LDAP_ALWAYS_UPDATE_USER', 'ldap.always_update_user', True
)
AUTH_LDAP_CACHE_TIMEOUT = get_setting(
'INVENTREE_LDAP_CACHE_TIMEOUT', 'ldap.cache_timeout', 3600, int
)
AUTH_LDAP_MIRROR_GROUPS = get_boolean_setting(
'INVENTREE_LDAP_MIRROR_GROUPS', 'ldap.mirror_groups', False
)
AUTH_LDAP_GROUP_OBJECT_CLASS = get_setting(
'INVENTREE_LDAP_GROUP_OBJECT_CLASS',
'ldap.group_object_class',
'groupOfUniqueNames',
str,
)
AUTH_LDAP_GROUP_SEARCH = django_auth_ldap.config.LDAPSearch(
get_setting('INVENTREE_LDAP_GROUP_SEARCH', 'ldap.group_search'),
ldap.SCOPE_SUBTREE,
f'(objectClass={AUTH_LDAP_GROUP_OBJECT_CLASS})',
)
AUTH_LDAP_GROUP_TYPE_CLASS = get_setting(
'INVENTREE_LDAP_GROUP_TYPE_CLASS',
'ldap.group_type_class',
'GroupOfUniqueNamesType',
str,
)
AUTH_LDAP_GROUP_TYPE_CLASS_ARGS = get_setting(
'INVENTREE_LDAP_GROUP_TYPE_CLASS_ARGS', 'ldap.group_type_class_args', [], list
)
AUTH_LDAP_GROUP_TYPE_CLASS_KWARGS = get_setting(
'INVENTREE_LDAP_GROUP_TYPE_CLASS_KWARGS',
'ldap.group_type_class_kwargs',
{'name_attr': 'cn'},
dict,
)
AUTH_LDAP_GROUP_TYPE = getattr(django_auth_ldap.config, AUTH_LDAP_GROUP_TYPE_CLASS)(
*AUTH_LDAP_GROUP_TYPE_CLASS_ARGS, **AUTH_LDAP_GROUP_TYPE_CLASS_KWARGS
)
AUTH_LDAP_REQUIRE_GROUP = get_setting(
'INVENTREE_LDAP_REQUIRE_GROUP', 'ldap.require_group'
)
AUTH_LDAP_DENY_GROUP = get_setting('INVENTREE_LDAP_DENY_GROUP', 'ldap.deny_group')
AUTH_LDAP_USER_FLAGS_BY_GROUP = get_setting(
'INVENTREE_LDAP_USER_FLAGS_BY_GROUP',
'ldap.user_flags_by_group',
default_value=None,
typecast=dict,
)
AUTH_LDAP_FIND_GROUP_PERMS = True
# Allow secure http developer server in debug mode # Allow secure http developer server in debug mode
if DEBUG: if DEBUG:
@@ -642,107 +553,11 @@ Configure the database backend based on the user-specified values.
logger.debug('Configuring database backend:') logger.debug('Configuring database backend:')
# Extract database configuration from the config.yaml file # Load database configuration from the config file and environment variables
db_config = CONFIG.get('database', None) if CONFIG else None database = db_backend.get_db_backend()
DB_ENGINE = database['ENGINE']
if not db_config: DATABASES = {'default': database}
db_config = {}
# Environment variables take preference over config file!
db_keys = ['ENGINE', 'NAME', 'USER', 'PASSWORD', 'HOST', 'PORT']
for key in db_keys:
# First, check the environment variables
env_key = f'INVENTREE_DB_{key}'
env_var = os.environ.get(env_key, None)
if env_var:
# Make use PORT is int
if key == 'PORT':
try:
env_var = int(env_var)
except ValueError:
logger.exception('Invalid number for %s: %s', env_key, env_var)
# Override configuration value
db_config[key] = env_var
# Check that required database configuration options are specified
required_keys = ['ENGINE', 'NAME']
# Ensure all database keys are upper case
db_config = {key.upper(): value for key, value in db_config.items()}
for key in required_keys:
if key not in db_config: # pragma: no cover
error_msg = (
f'Missing required database configuration value `INVENTREE_DB_{key}`'
)
logger.error(error_msg)
print('Error: ' + error_msg)
sys.exit(-1)
"""
Special considerations for the database 'ENGINE' setting.
It can be specified in config.yaml (or envvar) as either (for example):
- sqlite3
- django.db.backends.sqlite3
- django.db.backends.postgresql
"""
DB_ENGINE = db_config['ENGINE'].lower()
# Correct common misspelling
if DB_ENGINE == 'sqlite':
DB_ENGINE = 'sqlite3' # pragma: no cover
if DB_ENGINE in ['sqlite3', 'postgresql', 'mysql']:
# Prepend the required python module string
DB_ENGINE = f'django.db.backends.{DB_ENGINE}'
db_config['ENGINE'] = DB_ENGINE
db_name = db_config['NAME']
db_host = db_config.get('HOST', "''")
if 'sqlite' in DB_ENGINE:
db_name = str(Path(db_name).resolve())
db_config['NAME'] = db_name
logger.info('DB_ENGINE: %s', DB_ENGINE)
logger.info('DB_NAME: %s', db_name)
logger.info('DB_HOST: %s', db_host)
"""
In addition to base-level database configuration, we may wish to specify specific options to the database backend
Ref: https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-OPTIONS
"""
# 'OPTIONS' or 'options' can be specified in config.yaml
# Set useful sensible timeouts for a transactional webserver to communicate
# with its database server, that is, if the webserver is having issues
# connecting to the database server (such as a replica failover) don't sit and
# wait for possibly an hour or more, just tell the client something went wrong
# and let the client retry when they want to.
db_options = db_config.get('OPTIONS', db_config.get('options'))
if db_options is None:
db_options = {}
# Set database-specific options
db_backend.set_db_options(DB_ENGINE, db_options)
# Provide OPTIONS dict back to the database configuration dict
db_config['OPTIONS'] = db_options
# Set testing options for the database
db_config['TEST'] = {'CHARSET': 'utf8'}
# Set collation option for mysql test database
if 'mysql' in DB_ENGINE:
db_config['TEST']['COLLATION'] = 'utf8_general_ci' # pragma: no cover
DATABASES = {'default': db_config}
# login settings # login settings
REMOTE_LOGIN = get_boolean_setting( REMOTE_LOGIN = get_boolean_setting(
@@ -776,133 +591,28 @@ if SENTRY_ENABLED and SENTRY_DSN and not TESTING: # pragma: no cover
init_sentry(SENTRY_DSN, SENTRY_SAMPLE_RATE, inventree_tags) init_sentry(SENTRY_DSN, SENTRY_SAMPLE_RATE, inventree_tags)
# OpenTelemetry tracing # OpenTelemetry tracing
TRACING_ENABLED = get_boolean_setting( TRACING_ENABLED = (
'INVENTREE_TRACING_ENABLED', 'tracing.enabled', False get_boolean_setting('INVENTREE_TRACING_ENABLED', 'tracing.enabled', False)
and not isRunningBackup()
) )
TRACING_DETAILS: Optional[dict] = None
if TRACING_ENABLED and not isRunningBackup(): # pragma: no cover TRACING_DETAILS: Optional[dict] = tracing.configure_tracing(
from InvenTree.tracing import setup_instruments, setup_tracing DB_ENGINE, TRACING_ENABLED, inventree_tags
)
_t_endpoint = get_setting('INVENTREE_TRACING_ENDPOINT', 'tracing.endpoint', None)
_t_headers = get_setting('INVENTREE_TRACING_HEADERS', 'tracing.headers', None, dict)
if _t_headers is None:
_t_headers = {}
if _t_endpoint:
logger.info('OpenTelemetry tracing enabled')
TRACING_DETAILS = (
TRACING_DETAILS
if TRACING_DETAILS
else {
'endpoint': _t_endpoint,
'headers': _t_headers,
'resources_input': {
**{'inventree.env.' + k: v for k, v in inventree_tags.items()},
**get_setting(
'INVENTREE_TRACING_RESOURCES',
'tracing.resources',
default_value=None,
typecast=dict,
),
},
'console': get_boolean_setting(
'INVENTREE_TRACING_CONSOLE', 'tracing.console', False
),
'auth': get_setting(
'INVENTREE_TRACING_AUTH',
'tracing.auth',
default_value=None,
typecast=dict,
),
'is_http': get_setting(
'INVENTREE_TRACING_IS_HTTP', 'tracing.is_http', True
),
'append_http': get_boolean_setting(
'INVENTREE_TRACING_APPEND_HTTP', 'tracing.append_http', True
),
}
)
# Run tracing/logging instrumentation
setup_tracing(**TRACING_DETAILS)
setup_instruments(DB_ENGINE)
else:
logger.warning('OpenTelemetry tracing not enabled because endpoint is not set')
# endregion
# Cache configuration # Cache configuration
GLOBAL_CACHE_ENABLED = is_global_cache_enabled() GLOBAL_CACHE_ENABLED = is_global_cache_enabled()
CACHES = {'default': get_cache_config(GLOBAL_CACHE_ENABLED)} CACHES = {'default': get_cache_config(GLOBAL_CACHE_ENABLED)}
BACKGROUND_WORKER_TIMEOUT = int( # Background task processing with django-q
get_setting('INVENTREE_BACKGROUND_TIMEOUT', 'background.timeout', 90) Q_CLUSTER = worker.get_worker_config(
DB_ENGINE,
global_cache=GLOBAL_CACHE_ENABLED,
sentry_dsn=SENTRY_DSN if SENTRY_ENABLED and SENTRY_DSN else None,
debug=DEBUG,
) )
# Set the retry time for background workers to be slightly longer than the worker timeout, to ensure that workers have time to timeout before being retried
BACKGROUND_WORKER_RETRY = max(
int(get_setting('INVENTREE_BACKGROUND_RETRY', 'background.retry', 300)),
BACKGROUND_WORKER_TIMEOUT + 120,
)
# Prevent running multiple background workers if global cache is disabled
# This is to prevent scheduling conflicts due to the lack of a shared cache
BACKGROUND_WORKER_COUNT = (
int(get_setting('INVENTREE_BACKGROUND_WORKERS', 'background.workers', 4))
if GLOBAL_CACHE_ENABLED
else 1
)
# If running with SQLite, limit background worker threads to 1 to prevent database locking issues
if 'sqlite' in DB_ENGINE:
BACKGROUND_WORKER_COUNT = 1
BACKGROUND_WORKER_ATTEMPTS = int(
get_setting('INVENTREE_BACKGROUND_MAX_ATTEMPTS', 'background.max_attempts', 5)
)
# Check if '--sync' was passed in the command line
if '--sync' in sys.argv and '--noreload' in sys.argv and DEBUG:
SYNC_TASKS = True
else:
SYNC_TASKS = False
# Clean up sys.argv so Django doesn't complain about an unknown argument
if SYNC_TASKS:
sys.argv.remove('--sync')
# django-q background worker configuration
Q_CLUSTER = {
'name': 'InvenTree',
'label': 'Background Tasks',
'workers': BACKGROUND_WORKER_COUNT,
'timeout': BACKGROUND_WORKER_TIMEOUT,
'retry': BACKGROUND_WORKER_RETRY,
'max_attempts': BACKGROUND_WORKER_ATTEMPTS,
'save_limit': 1000,
'queue_limit': 50,
'catch_up': False,
'bulk': 10,
'orm': 'default',
'cache': 'default',
'sync': SYNC_TASKS,
'poll': 1.5,
}
# Configure django-q sentry integration
if SENTRY_ENABLED and SENTRY_DSN: # pragma: no cover
Q_CLUSTER['error_reporter'] = {'sentry': {'dsn': SENTRY_DSN}}
if GLOBAL_CACHE_ENABLED: # pragma: no cover
# If using external redis cache, make the cache the broker for Django Q
# as well
Q_CLUSTER['django_redis'] = 'worker'
SILENCED_SYSTEM_CHECKS = ['templates.E003', 'templates.W003'] SILENCED_SYSTEM_CHECKS = ['templates.E003', 'templates.W003']
# Password validation # Password validation
@@ -976,10 +686,10 @@ INTERNAL_EMAIL_BACKEND = get_setting(
) )
# SMTP backend # SMTP backend
EMAIL_HOST = get_setting('INVENTREE_EMAIL_HOST', 'email.host', '') EMAIL_HOST = get_setting('INVENTREE_EMAIL_HOST', 'email.host')
EMAIL_PORT = get_setting('INVENTREE_EMAIL_PORT', 'email.port', 25, typecast=int) EMAIL_PORT = get_setting('INVENTREE_EMAIL_PORT', 'email.port', 25, typecast=int)
EMAIL_HOST_USER = get_setting('INVENTREE_EMAIL_USERNAME', 'email.username', '') EMAIL_HOST_USER = get_setting('INVENTREE_EMAIL_USERNAME', 'email.username')
EMAIL_HOST_PASSWORD = get_setting('INVENTREE_EMAIL_PASSWORD', 'email.password', '') EMAIL_HOST_PASSWORD = get_setting('INVENTREE_EMAIL_PASSWORD', 'email.password')
EMAIL_USE_TLS = get_boolean_setting('INVENTREE_EMAIL_TLS', 'email.tls', False) EMAIL_USE_TLS = get_boolean_setting('INVENTREE_EMAIL_TLS', 'email.tls', False)
EMAIL_USE_SSL = get_boolean_setting('INVENTREE_EMAIL_SSL', 'email.ssl', False) EMAIL_USE_SSL = get_boolean_setting('INVENTREE_EMAIL_SSL', 'email.ssl', False)
# Anymail # Anymail
@@ -1150,16 +860,14 @@ LANGUAGE_COOKIE_SAMESITE = COOKIE_MODE
- Otherwise, use the value specified in the configuration file (or env var) - Otherwise, use the value specified in the configuration file (or env var)
""" """
COOKIE_SECURE = ( COOKIE_SECURE = (
False get_boolean_setting('INVENTREE_SESSION_COOKIE_SECURE', 'cookie.secure', False)
if DEBUG or SESSION_COOKIE_SAMESITE == 'None'
else (
SESSION_COOKIE_SAMESITE == 'None'
or get_boolean_setting(
'INVENTREE_SESSION_COOKIE_SECURE', 'cookie.secure', False
)
)
) )
# Override COOKIE_SECURE value in DEBUG mode
if DEBUG:
COOKIE_SECURE = False
CSRF_COOKIE_SECURE = COOKIE_SECURE CSRF_COOKIE_SECURE = COOKIE_SECURE
SESSION_COOKIE_SECURE = COOKIE_SECURE SESSION_COOKIE_SECURE = COOKIE_SECURE
LANGUAGE_COOKIE_SECURE = COOKIE_SECURE LANGUAGE_COOKIE_SECURE = COOKIE_SECURE
+1 -2
View File
@@ -29,7 +29,6 @@ from maintenance_mode.core import (
from opentelemetry import trace from opentelemetry import trace
from common.settings import get_global_setting, set_global_setting from common.settings import get_global_setting, set_global_setting
from InvenTree.config import get_setting
from plugin import registry from plugin import registry
from .version import isInvenTreeUpToDate from .version import isInvenTreeUpToDate
@@ -822,7 +821,7 @@ def check_for_migrations(force: bool = False, reload_registry: bool = True) -> b
set_pending_migrations(n) set_pending_migrations(n)
# Test if auto-updates are enabled # Test if auto-updates are enabled
if not force and not get_setting('INVENTREE_AUTO_UPDATE', 'auto_update'): if not force and not settings.AUTO_UPDATE:
logger.info('Auto-update is disabled - skipping migrations') logger.info('Auto-update is disabled - skipping migrations')
return False return False
+10 -1
View File
@@ -381,7 +381,16 @@ class ConfigSerializer(serializers.Serializer):
"""Return the configuration data as a dictionary.""" """Return the configuration data as a dictionary."""
if not isinstance(instance, str): if not isinstance(instance, str):
instance = list(instance.keys())[0] instance = list(instance.keys())[0]
return {'key': instance, **self.instance.get(instance)}
data = {'key': instance}
for k, v in self.instance.get(instance, {}).items():
if k == 'default_value':
# Skip sensitive default values
continue
data[k] = v
return data
class NotesImageSerializer(InvenTreeModelSerializer): class NotesImageSerializer(InvenTreeModelSerializer):
+14 -2
View File
@@ -1864,8 +1864,13 @@ def export_definitions(c, basedir: str = ''):
"""Export various definitions.""" """Export various definitions."""
if basedir != '' and basedir.endswith('/') is False: if basedir != '' and basedir.endswith('/') is False:
basedir += '/' basedir += '/'
base_path = Path(basedir, 'generated').resolve() base_path = Path(basedir, 'generated').resolve()
if not base_path.exists():
info(f'Creating export directory: {base_path}')
base_path.mkdir(parents=True, exist_ok=True)
filenames = [ filenames = [
base_path.joinpath('inventree_settings.json'), base_path.joinpath('inventree_settings.json'),
base_path.joinpath('inventree_tags.yml'), base_path.joinpath('inventree_tags.yml'),
@@ -2298,12 +2303,19 @@ def doc_schema(c):
help={ help={
'address': 'Host and port to run the server on (default: localhost:8080)', 'address': 'Host and port to run the server on (default: localhost:8080)',
'compile_schema': 'Compile the API schema documentation first (default: False)', 'compile_schema': 'Compile the API schema documentation first (default: False)',
'export_settings': 'Export settings definitions before starting the server (default: True)',
} }
) )
def docs_server(c, address='localhost:8080', compile_schema=False): def docs_server(
c,
address='localhost:8080',
compile_schema: bool = False,
export_settings: bool = True,
):
"""Start a local mkdocs server to view the documentation.""" """Start a local mkdocs server to view the documentation."""
# Extract settings definitions # Extract settings definitions
export_definitions(c, basedir='docs') if export_settings:
export_definitions(c, basedir='docs')
if compile_schema: if compile_schema:
doc_schema(c) doc_schema(c)