2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 12:06:44 +00:00

Fix URL generation error (#8630)

- Implement better helper function for handling URL generation
- Handle errors in said function
This commit is contained in:
Oliver 2024-12-06 14:44:13 +11:00 committed by GitHub
parent 9f120ef76f
commit 82bce192e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 42 additions and 33 deletions

View File

@ -3,8 +3,8 @@ import { IconUserStar } from '@tabler/icons-react';
import { useCallback, useMemo } from 'react';
import type { ModelType } from '../../enums/ModelType';
import { generateUrl } from '../../functions/urls';
import { useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
import { useUserState } from '../../states/UserState';
import { ModelInformationDict } from '../render/ModelType';
import { ActionButton } from './ActionButton';
@ -56,16 +56,14 @@ export default function AdminButton(props: Readonly<AdminButtonProps>) {
const openAdmin = useCallback(
(event: any) => {
const modelDef = ModelInformationDict[props.model];
const host = useLocalState.getState().host;
if (!modelDef.admin_url) {
return;
}
// Generate the URL for the admin interface
const url = new URL(
`${server.server.django_admin}${modelDef.admin_url}${props.id}/`,
host
const url = generateUrl(
`${server.server.django_admin}${modelDef.admin_url}${props.id}/`
);
if (event?.ctrlKey || event?.shiftKey) {

View File

@ -8,9 +8,9 @@ import { api } from '../../App';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import type { ModelType } from '../../enums/ModelType';
import { extractAvailableFields } from '../../functions/forms';
import { generateUrl } from '../../functions/urls';
import { useCreateApiFormModal } from '../../hooks/UseForm';
import { apiUrl } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
import { useUserSettingsState } from '../../states/SettingsState';
import type { ApiFormFieldSet } from '../forms/fields/ApiFormField';
import { ActionDropdown } from '../items/ActionDropdown';
@ -28,8 +28,6 @@ export function PrintingActions({
enableReports?: boolean;
modelType?: ModelType;
}) {
const { host } = useLocalState.getState();
const userSettings = useUserSettingsState();
const enabled = useMemo(() => items.length > 0, [items]);
@ -116,7 +114,7 @@ export function PrintingActions({
if (response.output) {
// An output file was generated
const url = new URL(response.output, host);
const url = generateUrl(response.output);
window.open(url.toString(), '_blank');
}
}
@ -154,7 +152,7 @@ export function PrintingActions({
if (response.output) {
// An output file was generated
const url = new URL(response.output, host);
const url = generateUrl(response.output);
window.open(url.toString(), '_blank');
}
}

View File

@ -6,6 +6,7 @@
import { Image, type ImageProps, Skeleton, Stack } from '@mantine/core';
import { useMemo } from 'react';
import { generateUrl } from '../../functions/urls';
import { useLocalState } from '../../states/LocalState';
interface ApiImageProps extends ImageProps {
@ -19,7 +20,7 @@ export function ApiImage(props: Readonly<ApiImageProps>) {
const { host } = useLocalState.getState();
const imageUrl = useMemo(() => {
return new URL(props.src, host).toString();
return generateUrl(props.src, host);
}, [host, props.src]);
return (

View File

@ -10,8 +10,7 @@ import {
IconPhoto
} from '@tabler/icons-react';
import { type ReactNode, useMemo } from 'react';
import { useLocalState } from '../../states/LocalState';
import { generateUrl } from '../../functions/urls';
/**
* Return an icon based on the provided filename
@ -61,16 +60,13 @@ export function AttachmentLink({
}>): ReactNode {
const text = external ? attachment : attachment.split('/').pop();
const host = useLocalState((s) => s.host);
const url = useMemo(() => {
if (external) {
return attachment;
}
const u = new URL(attachment, host);
return u.toString();
}, [host, attachment, external]);
return generateUrl(attachment);
}, [attachment, external]);
return (
<Group justify='left' gap='sm' wrap='nowrap'>

View File

@ -15,8 +15,8 @@ import { useQuery } from '@tanstack/react-query';
import { api } from '../../App';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { generateUrl } from '../../functions/urls';
import { apiUrl, useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
import { useUserState } from '../../states/UserState';
import { CopyButton } from '../buttons/CopyButton';
import { StylishText } from '../items/StylishText';
@ -35,7 +35,6 @@ export function AboutInvenTreeModal({
modalBody: string;
}>) {
const [user] = useUserState((state) => [state.user]);
const { host } = useLocalState.getState();
const [server] = useServerApiState((state) => [state.server]);
if (user?.is_staff != true)
@ -137,7 +136,7 @@ export function AboutInvenTreeModal({
{
ref: 'api',
title: <Trans>API Version</Trans>,
link: new URL('/api-doc/', host).toString(),
link: generateUrl('/api-doc/'),
copy: true
},
{

View File

@ -1,11 +1,9 @@
import { useLocalState } from '../../states/LocalState';
import { generateUrl } from '../../functions/urls';
/*
* Load an external plugin source from a URL.
*/
export async function loadExternalPluginSource(source: string) {
const host = useLocalState.getState().host;
source = source.trim();
// If no source is provided, clear the plugin content
@ -13,7 +11,7 @@ export async function loadExternalPluginSource(source: string) {
return null;
}
const url = new URL(source, host).toString();
const url = generateUrl(source);
const module = await import(/* @vite-ignore */ url)
.catch((error) => {

View File

@ -126,6 +126,7 @@ const icons = {
units: IconRulerMeasure,
keywords: IconTag,
status: IconInfoCircle,
status_custom_key: IconInfoCircle,
edit: IconEdit,
info: IconInfoCircle,
exclamation: IconExclamationCircle,
@ -291,6 +292,3 @@ export function InvenTreeIcon(props: Readonly<InvenTreeIconProps>) {
return <Icon {...props.iconProps} />;
}
function IconShapes(props: TablerIconProps): Element {
throw new Error('Function not implemented.');
}

View File

@ -1,6 +1,7 @@
import { ModelInformationDict } from '../components/render/ModelType';
import type { ModelType } from '../enums/ModelType';
import { base_url } from '../main';
import { useLocalState } from '../states/LocalState';
/**
* Returns the detail view URL for a given model type
@ -30,3 +31,26 @@ export function getDetailUrl(
console.error(`No detail URL found for model ${model} <${pk}>`);
return '';
}
/**
* Returns the edit view URL for a given model type
*/
export function generateUrl(url: string | URL, base?: string): string {
const { host } = useLocalState.getState();
let newUrl: string | URL = url;
try {
if (base) {
newUrl = new URL(url, base).toString();
} else if (host) {
newUrl = new URL(url, host).toString();
} else {
newUrl = url.toString();
}
} catch (e: any) {
console.error(`ERR: generateURL failed. url='${url}', base='${base}'`);
}
return newUrl.toString();
}

View File

@ -2,8 +2,8 @@ import { create } from 'zustand';
import { api } from '../App';
import { ApiEndpoints } from '../enums/ApiEndpoints';
import { generateUrl } from '../functions/urls';
import { apiUrl } from './ApiState';
import { useLocalState } from './LocalState';
type IconPackage = {
name: string;
@ -34,8 +34,6 @@ export const useIconState = create<IconState>()((set, get) => ({
fetchIcons: async () => {
if (get().hasLoaded) return;
const host = useLocalState.getState().host;
const packs = await api.get(apiUrl(ApiEndpoints.icons));
await Promise.all(
@ -43,8 +41,7 @@ export const useIconState = create<IconState>()((set, get) => ({
const fontName = `inventree-icon-font-${pack.prefix}`;
const src = Object.entries(pack.fonts as Record<string, string>)
.map(
([format, url]) =>
`url(${new URL(url, host).toString()}) format("${format}")`
([format, url]) => `url(${generateUrl(url)}) format("${format}")`
)
.join(',\n');
const font = new FontFace(fontName, `${src};`);