mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-02 13:57:30 +08:00
feat(frontend): add AuthenticatedImg component for authorized image requests (#16525)
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { Authorization } from '@/constants/authorization';
|
||||
import { restAPIv1 } from '@/utils/api';
|
||||
import { getAuthorization } from '@/utils/authorization-util';
|
||||
import { getSearchValue } from '@/utils/common-util';
|
||||
import classNames from 'classnames';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
||||
@@ -82,19 +81,36 @@ const fetchDocumentImage = (url: string, authorization: string) => {
|
||||
};
|
||||
};
|
||||
|
||||
// Check if a URL requires authentication (internal API URLs)
|
||||
// Only attach Authorization headers to same-origin requests to prevent token leakage
|
||||
const isAuthRequiredUrl = (url: string): boolean => {
|
||||
try {
|
||||
const parsedUrl = new URL(url, window.location.origin);
|
||||
if (parsedUrl.origin !== window.location.origin) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
parsedUrl.pathname.startsWith('/api/v1/') ||
|
||||
parsedUrl.pathname.includes('/documents/images/')
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const useDocumentImageUrl = (id: string, t?: string | number) => {
|
||||
const directUrl = useMemo(() => buildDocumentImageUrl(id, t), [id, t]);
|
||||
const [imageUrl, setImageUrl] = useState(() =>
|
||||
getAuthorization() && getSearchValue('shared_id') ? '' : directUrl,
|
||||
);
|
||||
const [imageUrl, setImageUrl] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
const authorization = getAuthorization();
|
||||
if (!authorization || !getSearchValue('shared_id')) {
|
||||
// For non-API URLs (e.g., base64, external URLs), use directly
|
||||
if (!isAuthRequiredUrl(directUrl)) {
|
||||
setImageUrl(directUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// For API URLs that require authentication, always fetch with auth headers
|
||||
const authorization = getAuthorization();
|
||||
let ignore = false;
|
||||
setImageUrl('');
|
||||
const { promise, release } = fetchDocumentImage(directUrl, authorization);
|
||||
@@ -120,6 +136,72 @@ export const useDocumentImageUrl = (id: string, t?: string | number) => {
|
||||
return imageUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to convert any authenticated URL to a blob URL for use in <img> tags.
|
||||
* Use this for thumbnail URLs or any other API URLs that require authentication.
|
||||
*/
|
||||
export const useAuthenticatedImageUrl = (url: string | undefined | null) => {
|
||||
const [imageUrl, setImageUrl] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!url || !isAuthRequiredUrl(url)) {
|
||||
setImageUrl(url || '');
|
||||
return;
|
||||
}
|
||||
|
||||
const authorization = getAuthorization();
|
||||
let cancelled = false;
|
||||
setImageUrl('');
|
||||
|
||||
const { promise, release } = fetchDocumentImage(url, authorization);
|
||||
promise
|
||||
.then((blobUrl) => {
|
||||
if (!cancelled) {
|
||||
setImageUrl(blobUrl);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
setImageUrl('');
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
release();
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
return imageUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component that renders an <img> tag with proper authentication for API URLs.
|
||||
* Use this instead of <img src={apiUrl}> when the URL requires authentication.
|
||||
*/
|
||||
export const AuthenticatedImg = ({
|
||||
src,
|
||||
alt,
|
||||
className,
|
||||
fallback,
|
||||
...props
|
||||
}: React.ImgHTMLAttributes<HTMLImageElement> & {
|
||||
fallback?: React.ReactNode;
|
||||
}) => {
|
||||
const authenticatedSrc = useAuthenticatedImageUrl(src);
|
||||
|
||||
if (!authenticatedSrc) return fallback ?? null;
|
||||
|
||||
return (
|
||||
<img
|
||||
src={authenticatedSrc}
|
||||
alt={alt}
|
||||
className={className}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Image = ({ id, t, label, className, ...props }: IImage) => {
|
||||
const src = useDocumentImageUrl(id, t);
|
||||
const imageElement = (
|
||||
|
||||
Reference in New Issue
Block a user