2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-04-25 04:23: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 = {
'global': ['SERVER_RESTART_REQUIRED'],
'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, {})
observed = observed_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:
| Name | Function | Value |
| ---- | -------- | ----- |
| INVENTREE_PLUGINS_ENABLED | Enables the use of 3rd party plugins | True |
| INVENTREE_PLUGIN_TESTING | Enables enables all plugins no matter of their active state in the db or built-in flag | True |
| INVENTREE_PLUGIN_TESTING_SETUP | Enables the url mixin | True |
{{ configtable() }}
{{ configsetting("INVENTREE_PLUGINS_ENABLED") }} Enables the use of 3rd party plugins |
{{ configsetting("INVENTREE_PLUGIN_TESTING") }} Enables enables all plugins no matter of their active state in the db or built-in flag |
{{ configsetting("INVENTREE_PLUGIN_TESTING_SETUP") }} Enables the url mixin |
### 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:
| Environment Variable |Configuration File | Description | More Info |
| --- | --- | --- | --- |
| 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) |
| 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) |
{{ 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 |
{{ configsetting("INVENTREE_SOCIAL_PROVIDERS") }} A *dict* of settings specific to the [installed providers](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:
+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.
| config key | ENV Variable | Description |
| --- | --- | --- |
| `ldap.enabled` | `INVENTREE_LDAP_ENABLED` | Set this to `True` to enable LDAP. |
| `ldap.debug` | `INVENTREE_LDAP_DEBUG` | Set this to `True` to activate debug mode, useful for troubleshooting ldap configurations. |
| `ldap.server_uri` | `INVENTREE_LDAP_SERVER_URI` | LDAP Server URI, e.g. `ldaps://example.org` |
| `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`) |
| `ldap.bind_dn` | `INVENTREE_LDAP_BIND_DN` | LDAP bind dn, e.g. `cn=admin,dc=example,dc=org` |
| `ldap.bind_password` | `INVENTREE_LDAP_BIND_PASSWORD` | LDAP bind password |
| `ldap.search_base_dn` | `INVENTREE_LDAP_SEARCH_BASE_DN` | LDAP search base dn, e.g. `cn=Users,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` |
| `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) |
| `ldap.search_filter_str`| `INVENTREE_LDAP_SEARCH_FILTER_STR` | LDAP search filter str, default: `uid=%(user)s` |
| `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"}` |
| `ldap.always_update_user` | `INVENTREE_LDAP_ALWAYS_UPDATE_USER` | Always update the user on each login, default: `true` |
| `ldap.cache_timeout` | `INVENTREE_LDAP_CACHE_TIMEOUT` | cache timeout to reduce traffic with LDAP server, default: `3600` (1h) |
| `ldap.group_search` | `INVENTREE_LDAP_GROUP_SEARCH` | Base LDAP DN for group searching; required to enable group features |
| `ldap.group_object_class` | `INVENTREE_LDAP_GROUP_OBJECT_CLASS` | The string to pass to the LDAP group search `(objectClass=<...>)`, default: `groupOfUniqueNames` |
| `ldap.mirror_groups` | `INVENTREE_LDAP_MIRROR_GROUPS` | If `True`, mirror a user's LDAP group membership in the Django database, default: `False` |
| `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'`|
| `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 `[]` |
| `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'}` |
| `ldap.require_group` | `INVENTREE_LDAP_REQUIRE_GROUP` | If set, users _must_ 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 |
| `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: `{}` |
{{ configtable() }}
{{ configsetting("INVENTREE_LDAP_ENABLED") }} Enable LDAP support |
{{ configsetting("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` |
| `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`) |
| `INVENTREE_LDAP_BIND_DN` | `ldap.bind_dn` | LDAP bind dn, e.g. `cn=admin,dc=example,dc=org` |
| `INVENTREE_LDAP_BIND_PASSWORD` | `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` |
| `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` |
| `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) |
| `INVENTREE_LDAP_SEARCH_FILTER_STR`| `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"}` |
| `INVENTREE_LDAP_ALWAYS_UPDATE_USER` | `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) |
| `INVENTREE_LDAP_GROUP_SEARCH` | `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` |
| `INVENTREE_LDAP_MIRROR_GROUPS` | `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'`|
| `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 `[]` |
| `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'}` |
| `INVENTREE_LDAP_REQUIRE_GROUP` | `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 |
| `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: `{}` |
## 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.
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 |
| --- | --- | --- |
| `tracing.enabled` | `INVENTREE_TRACING_ENABLED` | Set this to `True` to enable OpenTelemetry. |
| `tracing.endpoint` | `INVENTREE_TRACING_ENDPOINT` | General endpoint for information (not specific trace/log url) |
| `tracing.headers` | `INVENTREE_TRACING_HEADERS` | HTTP headers that should be send with every request (often used for authentication). Format as a dict. |
| `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) |
| `tracing.is_http` | `INVENTREE_TRACING_IS_HTTP` | Are the endpoints HTTP (True, default) or gRPC (False) |
| `tracing.append_http` | `INVENTREE_TRACING_APPEND_HTTP` | Append default url routes (v1) to `tracing.endpoint` |
| `tracing.console` | `INVENTREE_TRACING_CONSOLE` | Print out all exports (additionally) to the console for debugging. Do not use in production |
| `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. |
{{ configtable() }}
{{ configsetting("INVENTREE_TRACING_ENABLED") }} Enable OpenTelemetry |
{{ configsetting("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. |
{{ configsetting("INVENTREE_TRACING_AUTH") }} 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) |
{{ configsetting("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 |
{{ 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. |
## 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"
For more information on multi-site support, refer to the [Django documentation]({% include "django.html" %}/ref/contrib/sites/).
| Environment Variable | Config Key | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_SITE_MULTI | site_multi | Enable multiple sites | False |
| INVENTREE_SITE_ID | site_id | Specify a fixed site ID | _Not specified_ |
{{ configtable() }}
{{ configsetting("INVENTREE_SITE_MULTI") }} Enable multiple sites |
{{ configsetting("INVENTREE_SITE_ID") }} Specify a fixed site ID |
+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:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_BACKUP_STORAGE | backup_storage | Backup storage backend. Refer to the [storage backend documentation](#storage-backend). | django.core.files.storage.FileSystemStorage |
| INVENTREE_BACKUP_DIR | backup_dir | Backup storage directory. | *No default* |
| INVENTREE_BACKUP_OPTIONS | backup_options | Specific options for the selected storage backend (dict) | *No default* |
| INVENTREE_BACKUP_CONNECTOR_OPTIONS | backup_connector_options | Specific options for the database connector (dict). Refer to the [database connector options](#database-connector). | *No default* |
| 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 |
| INVENTREE_BACKUP_EMAIL_PREFIX | backup_email_prefix | Prefix for the subject line of backup-related emails. | `[InvenTree Backup]` |
| INVENTREE_BACKUP_GPG_RECIPIENT | backup_gpg_recipient | Specify GPG recipient if using encryption for backups. | *No default* |
| INVENTREE_BACKUP_DATE_FORMAT | backup_date_format | Date format string used to format timestamps in backup filenames. | `%Y-%m-%d-%H%M%S` |
| INVENTREE_BACKUP_DATABASE_FILENAME_TEMPLATE | backup_database_filename_template | Template string used to generate database backup filenames. | `InvenTree-db-{datetime}.{extension}` |
| INVENTREE_BACKUP_MEDIA_FILENAME_TEMPLATE | backup_media_filename_template | Template string used to generate media backup filenames. | `InvenTree-media-{datetime}.{extension}` |
| 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 |
{{ configtable() }}
{{ configsetting("INVENTREE_BACKUP_STORAGE") }} Backup storage backend. Refer to the [storage backend documentation](#storage-backend). |
{{ configsetting("INVENTREE_BACKUP_DIR") }} Backup storage directory. |
{{ configsetting("INVENTREE_BACKUP_OPTIONS") }} Specific options for the selected storage backend (dict) |
{{ configsetting("INVENTREE_BACKUP_CONNECTOR_OPTIONS") }} Specific options for the database connector (dict). Refer to the [database connector options](#database-connector) |
{{ 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. |
{{ configsetting("INVENTREE_BACKUP_EMAIL_PREFIX") }} Prefix for the subject line of backup-related emails. |
{{ configsetting("INVENTREE_BACKUP_GPG_RECIPIENT") }} Specify GPG recipient if using encryption for backups. |
{{ configsetting("INVENTREE_BACKUP_DATE_FORMAT") }} Date format string used to format timestamps in backup filenames. |
{{ configsetting("INVENTREE_BACKUP_DATABASE_FILENAME_TEMPLATE") }} Template string used to generate database backup filenames.
{{ configsetting("INVENTREE_BACKUP_MEDIA_FILENAME_TEMPLATE") }} Template string used to generate media backup filenames. |
{{ 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. |
### Storage Backend
+158 -168
View File
@@ -46,6 +46,9 @@ Environment variable settings generally use the `INVENTREE_` prefix, and are all
!!! warning "Available Variables"
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
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:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_SITE_URL | site_url | Specify a fixed site URL | *Not specified* |
| INVENTREE_TIMEZONE | timezone | Server timezone | UTC |
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the [django administrator interface]({% include "django.html" %}/ref/contrib/admin/) | True |
| INVENTREE_ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin |
| INVENTREE_LANGUAGE | language | Default language | en-us |
| INVENTREE_AUTO_UPDATE | auto_update | Database migrations will be run automatically | False |
{{ configtable() }}
{{ configsetting("INVENTREE_SITE_URL") }} Specify a fixed site URL |
{{ configsetting("INVENTREE_TIMEZONE") }} Server timezone |
{{ configsetting("INVENTREE_ADMIN_ENABLED") }} Enable the [django administrator interface]({% include "django.html" %}/ref/contrib/admin/) |
{{ configsetting("INVENTREE_ADMIN_URL") }} URL for accessing [admin interface](../settings/admin.md) |
{{ configsetting("INVENTREE_LANGUAGE") }} Default language |
{{ configsetting("INVENTREE_AUTO_UPDATE") }} Database migrations will be run automatically |
### Site URL
@@ -89,18 +91,17 @@ With "auto update" enabled, the InvenTree server will automatically apply databa
The following debugging / logging options are available:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_DEBUG | debug | Enable [debug mode](./index.md#debug-mode) | False |
| INVENTREE_DEBUG_QUERYCOUNT | debug_querycount | Enable support for [django-querycount](../develop/index.md#django-querycount) middleware. | False |
| INVENTREE_DEBUG_SILK | debug_silk | Enable support for [django-silk](../develop/index.md#django-silk) profiling tool. | False |
| INVENTREE_DEBUG_SILK_PROFILING | debug_silk_profiling | Enable detailed profiling in django-silk | False |
| INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False |
| INVENTREE_LOG_LEVEL | log_level | Set level of logging to terminal | WARNING |
| INVENTREE_JSON_LOG | json_log | log as json | False |
| INVENTREE_WRITE_LOG | write_log | Enable writing of log messages to file at config base | False |
| INVENTREE_CONSOLE_LOG | console_log | Enable logging to console | True |
| INVENTREE_SCHEMA_LEVEL | schema.level | Set level of added schema extensions detail (0-3) 0 = including no additional detail | 0 |
{{ configtable() }}
{{ configsetting("INVENTREE_DEBUG") }} Enable [debug mode](./index.md#debug-mode) |
{{ configsetting("INVENTREE_DB_LOGGING") }} Enable logging of database messages |
{{ configsetting("INVENTREE_LOG_LEVEL") }} Set level of logging to terminal |
{{ configsetting("INVENTREE_JSON_LOG") }} Log messages as json |
{{ configsetting("INVENTREE_WRITE_LOG") }} Enable writing of log messages to file at config base |
{{ configsetting("INVENTREE_CONSOLE_LOG") }} Enable logging to console |
{{ configsetting("INVENTREE_SCHEMA_LEVEL") }} Set level of added schema extensions detail (0-3) 0 = including no additional detail |
{{ configsetting("INVENTREE_DEBUG_QUERYCOUNT") }} Enable support for [django-querycount](../develop/index.md#django-querycount) middleware. |
{{ configsetting("INVENTREE_DEBUG_SILK") }} Enable support for [django-silk](../develop/index.md#django-silk) profiling tool. |
| `INVENTREE_DEBUG_SILK_PROFILING` | `debug_silk_profiling` | False | Enable detailed profiling in django-silk |
### Debug Mode
@@ -118,26 +119,24 @@ Depending on how your InvenTree installation is configured, you will need to pay
!!! danger "Not Secure"
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"
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 |
| --- | --- | --- | --- |
| INVENTREE_ALLOWED_HOSTS | allowed_hosts | List of allowed hosts | `*` |
| 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. |
| INVENTREE_CORS_ORIGIN_ALLOW_ALL | cors.allow_all | Allow all remote URLS for CORS checks | `False` |
| 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. |
| INVENTREE_CORS_ORIGIN_REGEX | cors.regex | List of regular expressions for CORS whitelisted URL patterns | *Empty list* |
| INVENTREE_CORS_ALLOW_CREDENTIALS | cors.allow_credentials | Allow cookies in cross-site requests | `True` |
| INVENTREE_SITE_LAX_PROTOCOL | site_lax_protocol | Ignore protocol mismatches on INVE-E7 site checks | `True` |
| INVENTREE_USE_X_FORWARDED_HOST | use_x_forwarded_host | Use forwarded host header | `False` |
| INVENTREE_USE_X_FORWARDED_PORT | use_x_forwarded_port | Use forwarded port header | `False` |
| INVENTREE_USE_X_FORWARDED_PROTO | use_x_forwarded_proto | Use forwarded protocol header | `False` |
| INVENTREE_SESSION_COOKIE_SECURE | cookie.secure | Enforce secure session cookies | `False` |
| 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 |
{{ configtable() }}
{{ configsetting("INVENTREE_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) |
{{ configsetting("INVENTREE_CORS_ORIGIN_ALLOW_ALL") }} Allow all remote URLS for CORS checks |
{{ 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) |
{{ configsetting("INVENTREE_CORS_ORIGIN_REGEX") }} List of regular expressions for CORS whitelisted URL patterns |
{{ configsetting("INVENTREE_CORS_ALLOW_CREDENTIALS") }} Allow cookies in cross-site requests |
{{ configsetting("INVENTREE_SITE_LAX_PROTOCOL") }} Ignore protocol mismatches on INVE-E7 site checks |
{{ configsetting("INVENTREE_USE_X_FORWARDED_HOST") }} Use forwarded host header |
{{ configsetting("INVENTREE_USE_X_FORWARDED_PORT") }} Use forwarded port header |
{{ configsetting("INVENTREE_USE_X_FORWARDED_PROTO") }} Use forwarded protocol header |
| `INVENTREE_X_FORWARDED_PROTO_NAME` | `x_forwarded_proto_name` | `HTTP_X_FORWARDED_PROTO` | Name of the header to use for forwarded protocol information |
{{ configsetting("INVENTREE_SESSION_COOKIE_SECURE") }} Enforce secure session cookies |
{{ 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
@@ -191,10 +190,9 @@ Django provides a powerful [administrator interface]({% include "django.html" %}
The following admin site configuration options are available:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the django administrator interface | True |
| INVENTREE_ADMIN_URL | admin_url | URL for accessing the admin interface | admin |
{{ configtable() }}
{{ configsetting("INVENTREE_ADMIN_ENABLED") }} Enable the django administrator interface |
{{ configsetting("INVENTREE_ADMIN_URL") }} URL for accessing the admin interface |
!!! warning "Security"
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:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_ADMIN_USER | admin_user | Admin account username | *Not specified* |
| INVENTREE_ADMIN_PASSWORD | admin_password | Admin account password | *Not specified* |
| INVENTREE_ADMIN_PASSWORD_FILE | admin_password_file | Admin account password file | *Not specified* |
| INVENTREE_ADMIN_EMAIL | admin_email |Admin account email address | *Not specified* |
{{ configtable() }}
{{ configsetting("INVENTREE_ADMIN_USER") }} Admin account username |
{{ configsetting("INVENTREE_ADMIN_PASSWORD") }} Admin account password |
{{ configsetting("INVENTREE_ADMIN_PASSWORD_FILE") }} Admin account password file |
{{ configsetting("INVENTREE_ADMIN_EMAIL") }} Admin account email address |
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.
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_SECRET_KEY | secret_key | Raw secret key value | *Not specified* |
| INVENTREE_SECRET_KEY_FILE | secret_key_file | File containing secret key value | *Not specified* |
| INVENTREE_OIDC_PRIVATE_KEY | oidc_private_key | Raw private key value | *Not specified* |
| INVENTREE_OIDC_PRIVATE_KEY_FILE | oidc_private_key_file | File containing private key value in PEM format | *Not specified* |
{{ configtable() }}
{{ configsetting("INVENTREE_SECRET_KEY") }} Raw secret key value |
{{ configsetting("INVENTREE_SECRET_KEY_FILE") }} File containing secret key value |
{{ configsetting("INVENTREE_OIDC_PRIVATE_KEY") }} Raw private key value |
{{ configsetting("INVENTREE_OIDC_PRIVATE_KEY_FILE", default="oidc.pem") }} File containing private key value in PEM format |
## Database Options
@@ -253,14 +249,14 @@ Database options are specified under the *database* heading in the configuration
The following database options can be configured:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_DB_ENGINE | database.ENGINE | Database backend | *Not specified* |
| INVENTREE_DB_NAME | database.NAME | Database name | *Not specified* |
| INVENTREE_DB_USER | database.USER | Database username (if required) | *Not specified* |
| INVENTREE_DB_PASSWORD | database.PASSWORD | Database password (if required) | *Not specified* |
| INVENTREE_DB_HOST | database.HOST | Database host address (if required) | *Not specified* |
| INVENTREE_DB_PORT | database.PORT | Database host port (if required) | *Not specified* |
{{ configtable() }}
{{ configsetting("INVENTREE_DB_ENGINE") }} Database backend |
{{ configsetting("INVENTREE_DB_NAME") }} Database name |
{{ configsetting("INVENTREE_DB_USER") }} Database username (if required) |
{{ configsetting("INVENTREE_DB_PASSWORD") }} Database password (if required) |
{{ configsetting("INVENTREE_DB_HOST") }} Database host address (if required) |
{{ configsetting("INVENTREE_DB_PORT") }} Database host port (if required) |
{{ configsetting("INVENTREE_DB_OPTIONS") }} Additional database options (as a JSON object) |
!!! 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.
@@ -269,22 +265,20 @@ The following database options can be configured:
If running with a PostgreSQL database backend, the following additional options are available:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_DB_TIMEOUT | database.timeout | Database connection timeout (s) | 2 |
| INVENTREE_DB_TCP_KEEPALIVES | database.tcp_keepalives | TCP keepalive | 1 |
| INVENTREE_DB_TCP_KEEPALIVES_IDLE | database.tcp_keepalives_idle | Idle TCP keepalive | 1 |
| INVENTREE_DB_TCP_KEEPALIVES_INTERVAL | database.tcp_keepalives_interval | TCP keepalive interval | 1|
| INVENTREE_DB_TCP_KEEPALIVES_COUNT | database.tcp_keepalives_count | TCP keepalive count | 5 |
| INVENTREE_DB_ISOLATION_SERIALIZABLE | database.serializable | Database isolation level configured to "serializable" | False |
{{ configtable() }}
{{ configsetting("INVENTREE_DB_TIMEOUT", default="2") }} Database connection timeout (s) |
| `INVENTREE_DB_TCP_KEEPALIVES` | database.tcp_keepalives | 1 | TCP keepalive |
| `INVENTREE_DB_TCP_KEEPALIVES_IDLE` | database.tcp_keepalives_idle | 1 | Idle TCP keepalive |
| `INVENTREE_DB_TCP_KEEPALIVES_INTERVAL` | database.tcp_keepalives_interval | 1| TCP keepalive interval |
| `INVENTREE_DB_TCP_KEEPALIVES_COUNT` | database.tcp_keepalives_count | 5 | TCP keepalive count |
| `INVENTREE_DB_ISOLATION_SERIALIZABLE` | database.serializable | False | Database isolation level configured to "serializable" |
### MySQL Settings
If running with a MySQL database backend, the following additional options are available:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_DB_ISOLATION_SERIALIZABLE | database.serializable | Database isolation level configured to "serializable" | False |
{{ configtable() }}
| `INVENTREE_DB_ISOLATION_SERIALIZABLE` | database.serializable | False | Database isolation level configured to "serializable" |
### 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:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_DB_TIMEOUT | database.timeout | Database connection timeout (s) | 10 |
| INVENTREE_DB_WAL_MODE | database.wal_mode | Enable Write-Ahead Logging (WAL) mode for SQLite databases | True |
{{ configtable() }}
{{ configsetting("INVENTREE_DB_TIMEOUT", default="10") }} Database connection timeout (s) |
| `INVENTREE_DB_WAL_MODE` | database.wal_mode | True | Enable Write-Ahead Logging (WAL) mode for SQLite databases |
## Caching
@@ -314,21 +307,21 @@ Enabling global caching requires connection to a redis server (which is separate
The following cache settings are available:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_CACHE_ENABLED | cache.enabled | Enable redis caching | False |
| INVENTREE_CACHE_HOST | cache.host | Cache server host | *Not specified* |
| INVENTREE_CACHE_PORT | cache.port | Cache server port | 6379 |
| INVENTREE_CACHE_PASSWORD | cache.password | Cache server password | none |
| INVENTREE_CACHE_USER | cache.user | Cache server username | none |
| INVENTREE_CACHE_DB | cache.db | Cache server database index | 0 |
| INVENTREE_CACHE_CONNECT_TIMEOUT | cache.connect_timeout | Cache connection timeout (seconds) | 3 |
| INVENTREE_CACHE_TIMEOUT | cache.timeout | Cache timeout (seconds) | 3 |
| INVENTREE_CACHE_TCP_KEEPALIVE | cache.tcp_keepalive | Cache TCP keepalive | True |
| INVENTREE_CACHE_KEEPALIVE_COUNT | cache.keepalive_count | Cache keepalive count | 5 |
| INVENTREE_CACHE_KEEPALIVE_IDLE | cache.keepalive_idle | Cache keepalive idle | 1 |
| INVENTREE_CACHE_KEEPALIVE_INTERVAL | cache.keepalive_interval | Cache keepalive interval | 1 |
| INVENTREE_CACHE_USER_TIMEOUT | cache.user_timeout | Cache user timeout | 1000 |
{{ configtable() }}
{{ configsetting("INVENTREE_CACHE_ENABLED") }} Enable redis caching |
{{ configsetting("INVENTREE_CACHE_HOST") }} Cache server host |
{{ configsetting("INVENTREE_CACHE_PORT") }} Cache server port |
{{ configsetting("INVENTREE_CACHE_PASSWORD") }} Cache server password |
{{ configsetting("INVENTREE_CACHE_USER") }} Cache server username |
{{ configsetting("INVENTREE_CACHE_DB") }} Cache server database index |
{{ configsetting("INVENTREE_CACHE_CONNECT_TIMEOUT") }} Cache connection timeout (seconds) |
{{ configsetting("INVENTREE_CACHE_TIMEOUT") }} Cache timeout (seconds) |
{{ configsetting("INVENTREE_CACHE_TCP_KEEPALIVE") }} Cache TCP keepalive |
{{ configsetting("INVENTREE_CACHE_KEEPALIVE_COUNT") }} Cache keepalive count |
{{ configsetting("INVENTREE_CACHE_KEEPALIVE_IDLE") }} Cache keepalive idle |
{{ configsetting("INVENTREE_CACHE_KEEPALIVE_INTERVAL") }} Cache keepalive interval |
{{ configsetting("INVENTREE_CACHE_USER_TIMEOUT") }} Cache user timeout |
!!! 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.
@@ -339,17 +332,16 @@ To enable [email functionality](../settings/email.md), email settings must be co
The following email settings are available:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_EMAIL_BACKEND | email.backend | Email backend module | django.core.mail.backends.smtp.EmailBackend |
| INVENTREE_EMAIL_HOST | email.host | Email server host | *Not specified* |
| INVENTREE_EMAIL_PORT | email.port | Email server port | 25 |
| INVENTREE_EMAIL_USERNAME | email.username | Email account username | *Not specified* |
| INVENTREE_EMAIL_PASSWORD | email.password | Email account password | *Not specified* |
| INVENTREE_EMAIL_TLS | email.tls | Enable STARTTLS support (commonly port 567) | False |
| INVENTREE_EMAIL_SSL | email.ssl | Enable legacy SSL/TLS support (commonly port 465) | False |
| INVENTREE_EMAIL_SENDER | email.sender | Sending email address | *Not specified* |
| INVENTREE_EMAIL_PREFIX | email.prefix | Prefix for subject text | [InvenTree] |
{{ configtable() }}
{{ configsetting("INVENTREE_EMAIL_BACKEND") }} Email backend module |
{{ configsetting("INVENTREE_EMAIL_HOST") }} Email server host |
{{ configsetting("INVENTREE_EMAIL_PORT") }} Email server port |
{{ configsetting("INVENTREE_EMAIL_USERNAME") }} Email account username |
{{ configsetting("INVENTREE_EMAIL_PASSWORD") }} Email account password |
{{ configsetting("INVENTREE_EMAIL_TLS") }} Enable STARTTLS support (commonly port 567) |
{{ configsetting("INVENTREE_EMAIL_SSL") }} Enable legacy SSL/TLS support (commonly port 465) |
{{ configsetting("INVENTREE_EMAIL_SENDER") }} Sending email address |
{{ configsetting("INVENTREE_EMAIL_PREFIX") }} Prefix for subject text |
### 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:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_STATIC_ROOT | static_root | [Static files](./processes.md#static-files) directory | *Not specified* |
| INVENTREE_MEDIA_ROOT | media_root | [Media files](./processes.md#media-files) directory | *Not specified* |
| INVENTREE_BACKUP_DIR | backup_dir | Backup files directory | *Not specified* |
{{ configtable() }}
{{ configsetting("INVENTREE_STATIC_ROOT") }} [Static files](./processes.md#static-files) directory |
{{ configsetting("INVENTREE_MEDIA_ROOT") }} [Media files](./processes.md#media-files) directory |
{{ configsetting("INVENTREE_BACKUP_DIR") }} Directory for backup files |
!!! tip "Serving 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.
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_STORAGE_TARGET | storage.target | Storage target to use for static and media files, valid options: local, s3, sftp | local |
{{ configtable() }}
{{ configsetting("INVENTREE_STORAGE_TARGET") }} Storage target to use for static and media files, valid options: local, s3, sftp |
#### S3
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_S3_ACCESS_KEY | storage.s3.access_key | Access key | *Not specified* |
| INVENTREE_S3_SECRET_KEY | storage.s3.secret_key | Secret key | *Not specified* |
| INVENTREE_S3_BUCKET_NAME | storage.s3.bucket_name | Bucket name, required by most providers | *Not specified* |
| INVENTREE_S3_REGION_NAME | storage.s3.region_name | S3 region name | *Not specified* |
| 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 | Sub-Location that should be used | inventree-server |
| 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 | Verify SSL certificate for S3 endpoint | True |
| INVENTREE_S3_OVERWRITE | storage.s3.overwrite | Overwrite existing files in S3 bucket | False |
| INVENTREE_S3_VIRTUAL | storage.s3.virtual | Use virtual addressing style - by default False -> `path` style, `virtual` style if True | False |
{{ configtable() }}
| `INVENTREE_S3_ACCESS_KEY` | storage.s3.access_key | *Not specified* | Access key |
| `INVENTREE_S3_SECRET_KEY` | storage.s3.secret_key | *Not specified* | Secret key |
| `INVENTREE_S3_BUCKET_NAME` | storage.s3.bucket_name | *Not specified* | Bucket name, required by most providers |
| `INVENTREE_S3_REGION_NAME` | storage.s3.region_name | *Not specified* | S3 region name |
| `INVENTREE_S3_ENDPOINT_URL` | storage.s3.endpoint_url | *Not specified* | Custom S3 endpoint URL, defaults to AWS endpoints if not set |
| `INVENTREE_S3_LOCATION` | storage.s3.location | inventree-server | Sub-Location that should be used |
| `INVENTREE_S3_DEFAULT_ACL` | storage.s3.default_acl | *Not specified* | Default ACL for uploaded files, defaults to provider default if not set |
| `INVENTREE_S3_VERIFY_SSL` | storage.s3.verify_ssl | True | Verify SSL certificate for S3 endpoint |
| `INVENTREE_S3_OVERWRITE` | storage.s3.overwrite | False | Overwrite existing files in S3 bucket |
| `INVENTREE_S3_VIRTUAL` | storage.s3.virtual | False | Use virtual addressing style - by default False -> `path` style, `virtual` style if True |
#### SFTP
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_SFTP_HOST | storage.sftp.host | SFTP host | *Not specified* |
| 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 | SFTP user ID - not required | *Not specified* |
| INVENTREE_SFTP_GID | storage.sftp.gid | SFTP group ID - not required | *Not specified* |
| INVENTREE_SFTP_LOCATION | storage.sftp.location | Sub-Location that should be used | inventree-server |
{{ configtable() }}
| `INVENTREE_SFTP_HOST` | storage.sftp.host | *Not specified* | SFTP host |
| `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_UID` | storage.sftp.uid | *Not specified* | SFTP user ID - not required |
| `INVENTREE_SFTP_GID` | storage.sftp.gid | *Not specified* | SFTP group ID - not required |
| `INVENTREE_SFTP_LOCATION` | storage.sftp.location | inventree-server | Sub-Location that should be used |
## 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.
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_MFA_ENABLED | mfa_enabled | Enable or disable multi-factor authentication support for the InvenTree server | True |
| INVENTREE_MFA_SUPPORTED_TYPES | mfa_supported_types | List of supported multi-factor authentication types | recovery_codes,totp,webauthn |
{{ configtable() }}
{{ configsetting("INVENTREE_MFA_ENABLED") }} Enable multi-factor authentication support for the InvenTree server |
{{ configsetting("INVENTREE_MFA_SUPPORTED_TYPES") }} List of supported multi-factor authentication types |
{{ configsetting("INVENTREE_USE_JWT") }} Enable support for JSON Web Tokens (JWT) for authentication |
### 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:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_LOGIN_CONFIRM_DAYS | login_confirm_days | Duration for which confirmation links are valid | 3 |
| INVENTREE_LOGIN_ATTEMPTS | login_attempts | Count of allowed login attempts before blocking user | 5 |
| 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* |
{{ configtable() }}
{{ configsetting("INVENTREE_LOGIN_CONFIRM_DAYS") }} Duration for which confirmation links are valid |
{{ configsetting("INVENTREE_LOGIN_ATTEMPTS") }} Count of allowed login attempts before blocking user |
{{ 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)) |
!!! 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*.
@@ -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):
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_BACKGROUND_WORKERS | background.workers | Number of background worker processes | 1 |
| INVENTREE_BACKGROUND_TIMEOUT | background.timeout | Timeout for background worker tasks (seconds) | 90 |
| INVENTREE_BACKGROUND_RETRY | background.retry | Time to wait before retrying a background task (seconds) | 300 |
| INVENTREE_BACKGROUND_MAX_ATTEMPTS | background.max_attempts | Maximum number of attempts for a background task | 5 |
{{ configtable() }}
{{ configsetting("INVENTREE_BACKGROUND_WORKERS") }} Number of background worker processes |
{{ configsetting("INVENTREE_BACKGROUND_TIMEOUT") }} Timeout for background worker tasks (seconds) |
{{ configsetting("INVENTREE_BACKGROUND_RETRY") }} Time to wait before retrying a background task (seconds) |
{{ configsetting("INVENTREE_BACKGROUND_MAX_ATTEMPTS") }} Maximum number of attempts for a background task |
## Sentry Integration
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 |
| --- | --- | --- | --- |
| INVENTREE_SENTRY_ENABLED | sentry_enabled | Enable sentry.io integration | False |
| INVENTREE_SENTRY_DSN | sentry_dsn | Sentry DSN (data source name) key | *Defaults to InvenTree developer key* |
| INVENTREE_SENTRY_SAMPLE_RATE | sentry_sample_rate | How often to send data samples | 0.1 |
{{ configtable() }}
{{ configsetting("INVENTREE_SENTRY_ENABLED") }} Enable sentry.io integration |
{{ configsetting("INVENTREE_SENTRY_DSN", default="Defaults to InvenTree developer key") }} Sentry DSN (data source name) key |
{{ configsetting("INVENTREE_SENTRY_SAMPLE_RATE") }} How often to send data samples (seconds) |
!!! info "Default DSN"
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:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_CUSTOM_LOGO | customize.logo | Path to custom logo in the static files directory | *Not specified* |
| INVENTREE_CUSTOM_SPLASH | customize.splash | Path to custom splash screen in the static files directory | *Not specified* |
| INVENTREE_CUSTOMIZE | customize.site_header | Custom site header in the Django admin | InvenTree Admin |
| INVENTREE_CUSTOMIZE | customize.login_message | Custom message for login page | *Not specified* |
| INVENTREE_CUSTOMIZE | customize.navbar_message | Custom message for navbar | *Not specified* |
{{ configtable() }}
{{ configsetting("INVENTREE_CUSTOM_LOGO") }} Path to custom logo in the static files directory |
{{ configsetting("INVENTREE_CUSTOM_SPLASH") }} Path to custom splash screen in the static files directory |
{{ configsetting("INVENTREE_SITE_HEADER") }} Custom header text for the django admin page |
{{ configsetting("INVENTREE_CUSTOMIZE") }} JSON object containing custom messages for the login page, navbar, and Django admin site |
The INVENTREE_CUSTOMIZE environment variable must contain a json object with the keys from the table above and
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:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_PLUGINS_ENABLED | plugins_enabled | Enable plugin support | False |
| INVENTREE_PLUGIN_NOINSTALL | plugin_noinstall | Disable Plugin installation via API - only use plugins.txt file | False |
| INVENTREE_PLUGIN_FILE | plugins_plugin_file | Location of plugin installation file | *Not specified* |
| INVENTREE_PLUGIN_DIR | plugins_plugin_dir | Location of external plugin directory | *Not specified* |
| INVENTREE_PLUGINS_MANDATORY | plugins_mandatory | List of [plugins which are considered mandatory](../plugins/index.md#mandatory-third-party-plugins) | *Not specified* |
| INVENTREE_PLUGIN_DEV_SLUG | plugin_dev.slug | Specify plugin to run in [development mode](../plugins/creator.md#backend-configuration) | *Not specified* |
| INVENTREE_PLUGIN_DEV_HOST | plugin_dev.host | Specify host for development mode plugin | http://localhost:5174 |
{{ configtable() }}
{{ configsetting("INVENTREE_PLUGINS_ENABLED") }} Enable plugin support |
{{ configsetting("INVENTREE_PLUGIN_NOINSTALL") }} Disable Plugin installation via API |
{{ configsetting("INVENTREE_PLUGIN_FILE") }} Location of plugin installation file |
| `INVENTREE_PLUGIN_DIR` | `plugin_dir` | *Not specified* | Location of external plugin directory |
{{ configsetting("INVENTREE_PLUGIN_RETRY") }} Number of tries to attempt loading a plugin before giving up |
{{ configsetting("INVENTREE_PLUGINS_MANDATORY") }} List of [plugins which are considered mandatory](../plugins/index.md#mandatory-third-party-plugins) |
{{ configsetting("INVENTREE_PLUGIN_DEV_SLUG") }} Specify plugin to run in [development mode](../plugins/creator.md#backend-configuration) |
{{ configsetting("INVENTREE_PLUGIN_DEV_HOST") }} Specify host for development mode plugin |
## 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.
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_GLOBAL_SETTINGS | global_settings | JSON object containing global settings overrides | *Not specified* |
{{ configtable() }}
{{ configsetting("INVENTREE_GLOBAL_SETTINGS") }} JSON object containing global settings overrides |
## 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
```
Alternatively, admin account details can be specified in the `.env` file, removing the need for this manual step:
| Variable | Description |
| --- | --- |
| INVENTREE_ADMIN_USER | Admin account username |
| INVENTREE_ADMIN_PASSWORD | Admin account password |
| INVENTREE_ADMIN_EMAIL | Admin account email address |
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.
!!! warning "Scrub Account Data"
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 |
| --- | --- |
| INVENTREE_WEB_ADDR | 0.0.0.0 |
| INVENTREE_WEB_PORT | 8000 |
| `INVENTREE_WEB_ADDR` | 0.0.0.0 |
| `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.
+30
View File
@@ -34,6 +34,7 @@ for key in [
print(f' - {key}: {val}')
# Cached settings dict values
global CONFIG_SETTINGS
global GLOBAL_SETTINGS
global USER_SETTINGS
global TAGS
@@ -64,6 +65,7 @@ with open(settings_file, encoding='utf-8') as sf:
GLOBAL_SETTINGS = settings['global']
USER_SETTINGS = settings['user']
CONFIG_SETTINGS = settings['config']
# Tags
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)
@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
def 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(),
'apiVersion': InvenTree.version.inventreeApiVersion(),
'worker_running': is_worker_running(),
'worker_count': settings.BACKGROUND_WORKER_COUNT,
'worker_count': settings.Q_CLUSTER['workers'],
'worker_pending_tasks': self.worker_pending_tasks(),
'plugins_enabled': settings.PLUGINS_ENABLED,
'plugins_install_disabled': settings.PLUGINS_INSTALL_DISABLED,
+12 -12
View File
@@ -94,17 +94,17 @@ def get_cache_config(global_cache: bool) -> dict:
Returns:
A dictionary containing the cache configuration options.
"""
if global_cache:
# Build Redis URL with optional password
password = cache_password()
user = cache_user() or ''
host = cache_host()
port = cache_port()
db = cache_db()
if password:
redis_url = (
f'redis://{user}:{password}@{cache_host()}:{cache_port()}/{cache_db()}'
)
redis_url = f'redis://{user}:{password}@{host}:{port}/{db}'
else:
redis_url = f'redis://{cache_host()}:{cache_port()}/{cache_db()}'
redis_url = f'redis://{host}:{port}/{db}'
keepalive_options = {
'TCP_KEEPCNT': cache_setting('keepalive_count', 5, typecast=int),
@@ -113,19 +113,15 @@ def get_cache_config(global_cache: bool) -> dict:
'TCP_USER_TIMEOUT': cache_setting('user_timeout', 1000, typecast=int),
}
return {
global_cache_config = {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': redis_url,
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'SOCKET_CONNECT_TIMEOUT': cache_setting(
'connect_timeout', 5, typecast=int
),
'SOCKET_CONNECT_TIMEOUT': cache_setting('connect_timeout', 5, typecast=int),
'SOCKET_TIMEOUT': cache_setting('timeout', 3, typecast=int),
'CONNECTION_POOL_KWARGS': {
'socket_keepalive': cache_setting(
'tcp_keepalive', True, typecast=bool
),
'socket_keepalive': cache_setting('tcp_keepalive', True, typecast=bool),
'socket_keepalive_options': {
# Only include options which are available on this platform
# e.g. MacOS does not have TCP_KEEPIDLE and TCP_USER_TIMEOUT
@@ -137,6 +133,10 @@ def get_cache_config(global_cache: bool) -> dict:
},
}
if global_cache:
# 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'}
+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):
"""Set lookup metadata for the setting."""
global CONFIG_LOOKUPS
key = env_var or config_key
CONFIG_LOOKUPS[key] = {
'env_var': env_var,
'config_key': config_key,
'default_value': default_value,
'source': source,
'accessed': datetime.datetime.now(),
}
@@ -544,13 +547,14 @@ def get_frontend_settings(debug=True):
'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
# This is the UI path e.g. '/web/'
if 'base_url' not in frontend_settings:
frontend_settings['base_url'] = (
get_setting('INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'web')
or 'web'
)
frontend_settings['base_url'] = base_url
# If provided, specify the API host
api_host = frontend_settings.get('api_host', None) or get_setting(
@@ -24,8 +24,9 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs):
"""Export settings information to a JSON file."""
from common.models import InvenTreeSetting, InvenTreeUserSetting
from InvenTree.config import CONFIG_LOOKUPS
settings = {'global': {}, 'user': {}}
settings = {'global': {}, 'user': {}, 'config': {}}
# Global settings
for key, setting in InvenTreeSetting.SETTINGS.items():
@@ -45,6 +46,18 @@ class Command(BaseCommand):
'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')
with open(filename, 'w', encoding='utf-8') as f:
@@ -1,5 +1,7 @@
"""Configuration settings specific to a particular database backend."""
from pathlib import Path
import structlog
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')
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):
"""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
+53 -345
View File
@@ -12,7 +12,6 @@ database setup in this file.
import logging
import os
import sys
from pathlib import Path
from typing import Optional
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
@@ -33,7 +32,16 @@ from InvenTree.version import checkMinPythonVersion, inventreeCommitHash
from users.oauth2_scopes import oauth2_scopes
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:
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
DOCKER = get_boolean_setting('INVENTREE_DOCKER', default_value=False)
AUTO_UPDATE = get_boolean_setting('INVENTREE_AUTO_UPDATE', 'auto_update', False)
# Configure logging settings
LOG_LEVEL = get_setting('INVENTREE_LOG_LEVEL', 'log_level', 'WARNING')
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 development settings
PLUGIN_DEV_SLUG = (
get_setting('INVENTREE_PLUGIN_DEV_SLUG', 'plugin_dev.slug') if DEBUG else None
)
PLUGIN_DEV_SLUG = get_setting('INVENTREE_PLUGIN_DEV_SLUG', 'plugin_dev.slug')
PLUGIN_DEV_HOST = get_setting(
'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
# Ref: https://silk.readthedocs.io/en/latest/
DJANGO_SILK_ENABLED = DEBUG and get_boolean_setting( # pragma: no cover
DJANGO_SILK_ENABLED = (
get_boolean_setting( # pragma: no cover
'INVENTREE_DEBUG_SILK', 'debug_silk', False
)
and DEBUG
)
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
# Ref: https://github.com/bradmontgomery/django-querycount
if DEBUG and get_boolean_setting( # pragma: no cover
if (
get_boolean_setting( # pragma: no cover
'INVENTREE_DEBUG_QUERYCOUNT', 'debug_querycount', False
)
and DEBUG
):
MIDDLEWARE.append('querycount.middleware.QueryCountMiddleware')
logger.debug('Running with debug_querycount middleware enabled')
@@ -437,14 +451,14 @@ AUTHENTICATION_BACKENDS = (
# LDAP support
LDAP_AUTH = get_boolean_setting('INVENTREE_LDAP_ENABLED', 'ldap.enabled', False)
if LDAP_AUTH: # pragma: no cover
import django_auth_ldap.config # type: ignore[unresolved-import]
import ldap # type: ignore[unresolved-import]
LDAP_DEBUG = (
get_boolean_setting('INVENTREE_LDAP_DEBUG', 'ldap.debug', False) and LDAP_AUTH
)
if LDAP_AUTH: # pragma: no cover
AUTHENTICATION_BACKENDS.append('django_auth_ldap.backend.LDAPBackend')
# debug mode to troubleshoot configuration
LDAP_DEBUG = get_boolean_setting('INVENTREE_LDAP_DEBUG', 'ldap.debug', False)
if LDAP_DEBUG:
if 'loggers' not in LOGGING:
LOGGING['loggers'] = {}
@@ -453,113 +467,10 @@ if LDAP_AUTH: # pragma: no cover
'handlers': DEFAULT_LOG_HANDLER,
}
# 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
# Determine LDAP settings
ldap_settings = ldap.get_ldap_config(debug=LDAP_DEBUG)
globals().update(ldap_settings)
# 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
if DEBUG:
@@ -642,107 +553,11 @@ Configure the database backend based on the user-specified values.
logger.debug('Configuring database backend:')
# Extract database configuration from the config.yaml file
db_config = CONFIG.get('database', None) if CONFIG else None
# Load database configuration from the config file and environment variables
database = db_backend.get_db_backend()
DB_ENGINE = database['ENGINE']
if not db_config:
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}
DATABASES = {'default': database}
# login settings
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)
# OpenTelemetry tracing
TRACING_ENABLED = get_boolean_setting(
'INVENTREE_TRACING_ENABLED', 'tracing.enabled', False
TRACING_ENABLED = (
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
from InvenTree.tracing import setup_instruments, setup_tracing
_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
TRACING_DETAILS: Optional[dict] = tracing.configure_tracing(
DB_ENGINE, TRACING_ENABLED, inventree_tags
)
# Cache configuration
GLOBAL_CACHE_ENABLED = is_global_cache_enabled()
CACHES = {'default': get_cache_config(GLOBAL_CACHE_ENABLED)}
BACKGROUND_WORKER_TIMEOUT = int(
get_setting('INVENTREE_BACKGROUND_TIMEOUT', 'background.timeout', 90)
# Background task processing with django-q
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']
# Password validation
@@ -976,10 +686,10 @@ INTERNAL_EMAIL_BACKEND = get_setting(
)
# 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_HOST_USER = get_setting('INVENTREE_EMAIL_USERNAME', 'email.username', '')
EMAIL_HOST_PASSWORD = get_setting('INVENTREE_EMAIL_PASSWORD', 'email.password', '')
EMAIL_HOST_USER = get_setting('INVENTREE_EMAIL_USERNAME', 'email.username')
EMAIL_HOST_PASSWORD = get_setting('INVENTREE_EMAIL_PASSWORD', 'email.password')
EMAIL_USE_TLS = get_boolean_setting('INVENTREE_EMAIL_TLS', 'email.tls', False)
EMAIL_USE_SSL = get_boolean_setting('INVENTREE_EMAIL_SSL', 'email.ssl', False)
# Anymail
@@ -1150,16 +860,14 @@ LANGUAGE_COOKIE_SAMESITE = COOKIE_MODE
- Otherwise, use the value specified in the configuration file (or env var)
"""
COOKIE_SECURE = (
False
if DEBUG
else (
SESSION_COOKIE_SAMESITE == 'None'
or get_boolean_setting(
'INVENTREE_SESSION_COOKIE_SECURE', 'cookie.secure', False
)
)
get_boolean_setting('INVENTREE_SESSION_COOKIE_SECURE', 'cookie.secure', False)
or SESSION_COOKIE_SAMESITE == 'None'
)
# Override COOKIE_SECURE value in DEBUG mode
if DEBUG:
COOKIE_SECURE = False
CSRF_COOKIE_SECURE = COOKIE_SECURE
SESSION_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 common.settings import get_global_setting, set_global_setting
from InvenTree.config import get_setting
from plugin import registry
from .version import isInvenTreeUpToDate
@@ -822,7 +821,7 @@ def check_for_migrations(force: bool = False, reload_registry: bool = True) -> b
set_pending_migrations(n)
# 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')
return False
+10 -1
View File
@@ -381,7 +381,16 @@ class ConfigSerializer(serializers.Serializer):
"""Return the configuration data as a dictionary."""
if not isinstance(instance, str):
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):
+13 -1
View File
@@ -1864,8 +1864,13 @@ def export_definitions(c, basedir: str = ''):
"""Export various definitions."""
if basedir != '' and basedir.endswith('/') is False:
basedir += '/'
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 = [
base_path.joinpath('inventree_settings.json'),
base_path.joinpath('inventree_tags.yml'),
@@ -2298,11 +2303,18 @@ def doc_schema(c):
help={
'address': 'Host and port to run the server on (default: localhost:8080)',
'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."""
# Extract settings definitions
if export_settings:
export_definitions(c, basedir='docs')
if compile_schema: