Coverage for src/backend/InvenTree/InvenTree/urls.py: 85%
51 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1"""Top-level URL lookup for InvenTree application.
3Passes URL lookup downstream to each app as required.
4"""
6from django.conf import settings
7from django.conf.urls.static import static
8from django.contrib import admin
9from django.urls import include, path, re_path
10from django.views.decorators.csrf import csrf_exempt
11from django.views.generic.base import RedirectView
13from allauth.headless.urls import Client, build_urlpatterns
14from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView
15from flags.urls import flagged_path
16from oauth2_provider import urls as oauth2_urls
17from sesame.views import LoginView
19import build.api
20import common.api
21import company.api
22import importer.api
23import InvenTree.logging # noqa: F401 - ensure logging handlers are registered
24import machine.api
25import order.api
26import part.api
27import plugin.api
28import report.api
29import stock.api
30import users.api
31from plugin.urls import get_plugin_urls
32from web.urls import cui_compatibility_urls
33from web.urls import urlpatterns as platform_urls
35from .api import (
36 APISearchView,
37 InfoView,
38 LicenseView,
39 NotFoundView,
40 VersionTextView,
41 VersionView,
42)
43from .config import get_setting
44from .magic_login import GetSimpleLoginView
45from .views import auth_request
47# Set admin header from config or use default
48admin.site.site_header = get_setting(
49 'INVENTREE_SITE_HEADER', 'customize.site_header', 'InvenTree Admin'
50)
53apipatterns = [
54 # Global search
55 path('admin/', include(common.api.admin_api_urls)),
56 path('bom/', include(part.api.bom_api_urls)),
57 path('build/', include(build.api.build_api_urls)),
58 path('company/', include(company.api.company_api_urls)),
59 path('importer/', include(importer.api.importer_api_urls)),
60 path('label/', include(report.api.label_api_urls)),
61 path('machine/', include(machine.api.machine_api_urls)),
62 path('order/', include(order.api.order_api_urls)),
63 path('part/', include(part.api.part_api_urls)),
64 path('report/', include(report.api.report_api_urls)),
65 path('search/', APISearchView.as_view(), name='api-search'),
66 path('settings/', include(common.api.settings_api_urls)),
67 path('stock/', include(stock.api.stock_api_urls)),
68 path(
69 'generate/',
70 include([
71 path(
72 'batch-code/',
73 stock.api.GenerateBatchCode.as_view(),
74 name='api-generate-batch-code',
75 ),
76 path(
77 'serial-number/',
78 stock.api.GenerateSerialNumber.as_view(),
79 name='api-generate-serial-number',
80 ),
81 ]),
82 ),
83 path('user/', include(users.api.user_urls)),
84 # Plugin endpoints
85 path('', include(plugin.api.plugin_api_urls)),
86 # Common endpoints endpoint
87 path('', include(common.api.common_api_urls)),
88 # OpenAPI Schema
89 path(
90 'schema/',
91 SpectacularAPIView.as_view(custom_settings={'SCHEMA_PATH_PREFIX': '/api/'}),
92 name='schema',
93 ),
94 # InvenTree information endpoints
95 path('license/', LicenseView.as_view(), name='api-license'), # license info
96 path(
97 'version-text', VersionTextView.as_view(), name='api-version-text'
98 ), # version text
99 path('version/', VersionView.as_view(), name='api-version'), # version info
100 path('', InfoView.as_view(), name='api-inventree-info'), # server info
101 # Auth API endpoints
102 path(
103 'auth/',
104 include([
105 path(
106 'login-redirect/',
107 users.api.LoginRedirect.as_view(),
108 name='api-login-redirect',
109 ),
110 path(
111 '',
112 include(
113 (build_urlpatterns(Client.BROWSER), 'headless'), namespace='browser'
114 ),
115 ), # Allauth headless logic (only the browser client is included as we only use sessions based auth there)
116 ]),
117 ),
118 # Magic login URLs
119 path(
120 'email/generate/',
121 csrf_exempt(GetSimpleLoginView().as_view()),
122 name='sesame-generate',
123 ),
124 path('email/login/', LoginView.as_view(), name='sesame-login'),
125 # Unknown endpoint
126 re_path(r'^.*$', NotFoundView.as_view(), name='api-404'),
127]
130backendpatterns = [
131 path(
132 'auth/', include('rest_framework.urls', namespace='rest_framework')
133 ), # Used for (DRF) browsable API auth
134 path(
135 'auth/', auth_request, name='auth-check'
136 ), # Used for proxies to check if user is authenticated
137 path('accounts/', include('allauth.urls')),
138 # OAuth2
139 flagged_path('OIDC', 'o/', include(oauth2_urls)),
140 path(
141 'accounts/login/',
142 RedirectView.as_view(url=f'/{settings.FRONTEND_URL_BASE}', permanent=False),
143 name='account_login',
144 ), # Add a redirect for login views
145 path('anymail/', include('anymail.urls')), # Emails
146]
148urlpatterns = []
150if settings.INVENTREE_ADMIN_ENABLED: 150 ↛ 159line 150 didn't jump to line 159 because the condition on line 150 was always true
151 admin_url = settings.INVENTREE_ADMIN_URL
153 urlpatterns += [
154 path(f'{admin_url}/error_log/', include('error_report.urls')),
155 path(f'{admin_url}/doc/', include('django.contrib.admindocs.urls')),
156 path(f'{admin_url}/', admin.site.urls, name='inventree-admin'),
157 ]
159urlpatterns += backendpatterns
160urlpatterns += [ # API URLs
161 path('api/', include(apipatterns)),
162 path('api-doc/', SpectacularRedocView.as_view(url_name='schema'), name='api-doc'),
163]
164urlpatterns += platform_urls
166# Append custom plugin URLs (if custom plugin support is enabled)
167if settings.PLUGINS_ENABLED: 167 ↛ 168line 167 didn't jump to line 168 because the condition on line 167 was never true
168 urlpatterns.append(get_plugin_urls())
170# Server running in "DEBUG" mode?
171if settings.DEBUG: 171 ↛ 173line 171 didn't jump to line 173 because the condition on line 171 was never true
172 # Static file access
173 urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
175 # Media file access
176 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
178# Redirect for favicon.ico
179urlpatterns.append(
180 path(
181 'favicon.ico',
182 RedirectView.as_view(url=f'{settings.STATIC_URL}img/favicon/favicon.ico'),
183 )
184)
186# Compatibility layer for old (CUI) URLs
187if settings.FRONTEND_SETTINGS.get('url_compatibility'): 187 ↛ 190line 187 didn't jump to line 190 because the condition on line 187 was always true
188 urlpatterns += cui_compatibility_urls(settings.FRONTEND_URL_BASE)
190if settings.DJANGO_SILK_ENABLED: 190 ↛ 191line 190 didn't jump to line 191 because the condition on line 190 was never true
191 urlpatterns += [path('silk/', include('silk.urls', namespace='silk'))]
193# Send any unknown URLs to the index page
194urlpatterns += [
195 re_path(
196 r'^.*$',
197 RedirectView.as_view(url=f'/{settings.FRONTEND_URL_BASE}', permanent=False),
198 name='index',
199 )
200]