Просмотр исходного кода

Fixes #7091: Ensure API requests from the UI are aware of `BASE_PATH`

thatmattlove 4 лет назад
Родитель
Сommit
679bbd3e76

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/config.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/config.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/jobs.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/jobs.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/lldp.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/lldp.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/status.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/status.js.map


+ 42 - 4
netbox/project-static/src/util.ts

@@ -11,7 +11,7 @@ type InferredProps<
   // Element name.
   T extends keyof HTMLElementTagNameMap,
   // Element type.
-  E extends HTMLElementTagNameMap[T] = HTMLElementTagNameMap[T]
+  E extends HTMLElementTagNameMap[T] = HTMLElementTagNameMap[T],
 > = Partial<Record<keyof E, E[keyof E]>>;
 
 export function isApiError(data: Record<string, unknown>): data is APIError {
@@ -94,7 +94,7 @@ export function isElement(obj: Element | null | undefined): obj is Element {
 /**
  * Retrieve the CSRF token from cookie storage.
  */
-export function getCsrfToken(): string {
+function getCsrfToken(): string {
   const { csrftoken: csrfToken } = Cookie.parse(document.cookie);
   if (typeof csrfToken === 'undefined') {
     throw new Error('Invalid or missing CSRF token');
@@ -102,8 +102,45 @@ export function getCsrfToken(): string {
   return csrfToken;
 }
 
+/**
+ * Get the NetBox `settings.BASE_PATH` from the `<html/>` element's data attributes.
+ *
+ * @returns If there is no `BASE_PATH` specified, the return value will be `''`.
+ */ function getBasePath(): string {
+  const value = document.documentElement.getAttribute('data-netbox-base-path');
+  if (value === null) {
+    return '';
+  }
+  return value;
+}
+
+/**
+ * Build a NetBox URL that includes `settings.BASE_PATH` and enforces leading and trailing slashes.
+ *
+ * @example
+ * ```js
+ * // With a BASE_PATH of 'netbox/'
+ * const url = buildUrl('/api/dcim/devices');
+ * console.log(url);
+ * // => /netbox/api/dcim/devices/
+ * ```
+ *
+ * @param path Relative path _after_ (excluding) the `BASE_PATH`.
+ */
+function buildUrl(path: string): string {
+  const basePath = getBasePath();
+  let combined = [...basePath.split('/'), ...path.split('/')].filter(p => p);
+  if (combined[0] !== '/') {
+    combined = ['', ...combined];
+  }
+  if (combined[combined.length - 1] !== '/') {
+    combined = [...combined, ''];
+  }
+  return combined.join('/');
+}
+
 export async function apiRequest<R extends Dict, D extends ReqData = undefined>(
-  url: string,
+  path: string,
   method: Method,
   data?: D,
 ): Promise<APIResponse<R>> {
@@ -115,6 +152,7 @@ export async function apiRequest<R extends Dict, D extends ReqData = undefined>(
     body = JSON.stringify(data);
     headers.set('content-type', 'application/json');
   }
+  const url = buildUrl(path);
 
   const res = await fetch(url, { method, body, headers, credentials: 'same-origin' });
   const contentType = res.headers.get('Content-Type');
@@ -367,7 +405,7 @@ export function createElement<
   // Element props.
   P extends InferredProps<T>,
   // Child element type.
-  C extends HTMLElement = HTMLElement
+  C extends HTMLElement = HTMLElement,
 >(tag: T, properties: P | null, classes: string[], children: C[] = []): HTMLElementTagNameMap[T] {
   // Create the base element.
   const element = document.createElement<T>(tag);

+ 1 - 0
netbox/templates/base/base.html

@@ -5,6 +5,7 @@
 <html
   lang="en"
   data-netbox-path="{{ request.path }}"
+  data-netbox-base-path="{% base_path %}"
   {% if preferences|get_key:'ui.colormode' == 'dark'%}
     data-netbox-color-mode="dark"
   {% else %}

+ 30 - 22
netbox/utilities/templatetags/helpers.py

@@ -304,28 +304,6 @@ def get_item(value: object, attr: str) -> Any:
     return value[attr]
 
 
-#
-# Tags
-#
-
-@register.simple_tag()
-def querystring(request, **kwargs):
-    """
-    Append or update the page number in a querystring.
-    """
-    querydict = request.GET.copy()
-    for k, v in kwargs.items():
-        if v is not None:
-            querydict[k] = str(v)
-        elif k in querydict:
-            querydict.pop(k)
-    querystring = querydict.urlencode(safe='/')
-    if querystring:
-        return '?' + querystring
-    else:
-        return ''
-
-
 @register.filter
 def status_from_tag(tag: str = "info") -> str:
     """
@@ -355,6 +333,36 @@ def icon_from_status(status: str = "info") -> str:
     return icon_map.get(status.lower(), 'information')
 
 
+#
+# Tags
+#
+
+@register.simple_tag()
+def querystring(request, **kwargs):
+    """
+    Append or update the page number in a querystring.
+    """
+    querydict = request.GET.copy()
+    for k, v in kwargs.items():
+        if v is not None:
+            querydict[k] = str(v)
+        elif k in querydict:
+            querydict.pop(k)
+    querystring = querydict.urlencode(safe='/')
+    if querystring:
+        return '?' + querystring
+    else:
+        return ''
+
+
+@register.simple_tag()
+def base_path():
+    """
+    Access `BASE_PATH` in templates.
+    """
+    return settings.BASE_PATH
+
+
 @register.inclusion_tag('utilities/templatetags/utilization_graph.html')
 def utilization_graph(utilization, warning_threshold=75, danger_threshold=90):
     """

Некоторые файлы не были показаны из-за большого количества измененных файлов