Web: Fixed the download and preview file not authorized. (#3652)
https://github.com/infiniflow/ragflow/issues/3651 ### What problem does this PR solve? _Briefly describe what this PR aims to solve. Include background context that will help reviewers understand the purpose of the PR._ ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
parent
efae7afd62
commit
fc38afcec4
@ -323,7 +323,7 @@ def rename():
|
||||
|
||||
|
||||
@manager.route('/get/<file_id>', methods=['GET'])
|
||||
# @login_required
|
||||
@login_required
|
||||
def get(file_id):
|
||||
try:
|
||||
e, file = FileService.get_by_id(file_id)
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@ant-design/pro-components": "^2.6.46",
|
||||
"@ant-design/pro-layout": "^7.17.16",
|
||||
"@antv/g": "^6.1.11",
|
||||
"@antv/g6": "^5.0.10",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@js-preview/excel": "^1.7.8",
|
||||
@ -72,6 +73,7 @@
|
||||
"react-i18next": "^14.0.0",
|
||||
"react-infinite-scroll-component": "^6.1.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-pdf": "^9.1.1",
|
||||
"react-pdf-highlighter": "^6.1.0",
|
||||
"react-string-replace": "^1.1.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
|
||||
@ -123,6 +123,6 @@ export {
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuViewport,
|
||||
navigationMenuTriggerStyle,
|
||||
NavigationMenuViewport,
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ import { ResponseType } from '@/interfaces/database/base';
|
||||
import { IFolder } from '@/interfaces/database/file-manager';
|
||||
import { IConnectRequestBody } from '@/interfaces/request/file-manager';
|
||||
import fileManagerService from '@/services/file-manager-service';
|
||||
import { downloadFileFromBlob } from '@/utils/file-util';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { PaginationProps, UploadFile, message } from 'antd';
|
||||
import React, { useCallback } from 'react';
|
||||
@ -114,6 +115,39 @@ export const useDeleteFile = () => {
|
||||
return { data, loading, deleteFile: mutateAsync };
|
||||
};
|
||||
|
||||
export const useDownloadFile = () => {
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: ['downloadFile'],
|
||||
mutationFn: async (params: { id: string; filename?: string }) => {
|
||||
const response = await fileManagerService.getFile({}, params.id);
|
||||
const blob = new Blob([response.data], { type: response.data.type });
|
||||
downloadFileFromBlob(blob, params.filename);
|
||||
},
|
||||
});
|
||||
return { data, loading, downloadFile: mutateAsync };
|
||||
};
|
||||
|
||||
export const useLoadFile = () => {
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
error,
|
||||
} = useMutation({
|
||||
mutationKey: ['downloadFile'],
|
||||
mutationFn: async (params: { id: string }) => {
|
||||
const response = await fileManagerService.getFile({}, params.id);
|
||||
const blob = new Blob([response.data], { type: response.data.type });
|
||||
return blob;
|
||||
},
|
||||
});
|
||||
return { data, loading, loadFile: mutateAsync, error };
|
||||
};
|
||||
|
||||
export const useRenameFile = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -231,7 +231,8 @@ export default {
|
||||
maxTokensMessage: 'El máximo de tokens es obligatorio',
|
||||
maxTokensTip:
|
||||
'Esto establece la longitud máxima de la salida del modelo, medida en el número de tokens (palabras o piezas de palabras).',
|
||||
maxTokensInvalidMessage: 'Por favor, ingresa un número válido para Max Tokens.',
|
||||
maxTokensInvalidMessage:
|
||||
'Por favor, ingresa un número válido para Max Tokens.',
|
||||
maxTokensMinMessage: 'Max Tokens no puede ser menor que 0.',
|
||||
quote: 'Mostrar cita',
|
||||
quoteTip: '¿Debe mostrarse la fuente del texto original?',
|
||||
@ -284,7 +285,8 @@ export default {
|
||||
maxTokensMessage: 'El máximo de tokens es obligatorio',
|
||||
maxTokensTip:
|
||||
'Esto establece la longitud máxima de la salida del modelo, medida en el número de tokens (palabras o piezas de palabras).',
|
||||
maxTokensInvalidMessage: 'Por favor, ingresa un número válido para Max Tokens.',
|
||||
maxTokensInvalidMessage:
|
||||
'Por favor, ingresa un número válido para Max Tokens.',
|
||||
maxTokensMinMessage: 'Max Tokens no puede ser menor que 0.',
|
||||
password: 'Contraseña',
|
||||
passwordDescription:
|
||||
|
||||
@ -401,7 +401,8 @@ export default {
|
||||
maxTokensMessage: 'Token Maksimum diperlukan',
|
||||
maxTokensTip:
|
||||
'Ini menetapkan panjang maksimum keluaran model, diukur dalam jumlah token (kata atau potongan kata).',
|
||||
maxTokensInvalidMessage: 'Silakan masukkan angka yang valid untuk Max Tokens.',
|
||||
maxTokensInvalidMessage:
|
||||
'Silakan masukkan angka yang valid untuk Max Tokens.',
|
||||
maxTokensMinMessage: 'Max Tokens tidak boleh kurang dari 0.',
|
||||
quote: 'Tampilkan Kutipan',
|
||||
quoteTip: 'Haruskah sumber teks asli ditampilkan?',
|
||||
@ -456,7 +457,8 @@ export default {
|
||||
maxTokensMessage: 'Token Maksimum diperlukan',
|
||||
maxTokensTip:
|
||||
'Ini menetapkan panjang maksimum keluaran model, diukur dalam jumlah token (kata atau potongan kata).',
|
||||
maxTokensInvalidMessage: 'Silakan masukkan angka yang valid untuk Max Tokens.',
|
||||
maxTokensInvalidMessage:
|
||||
'Silakan masukkan angka yang valid untuk Max Tokens.',
|
||||
maxTokensMinMessage: 'Max Tokens tidak boleh kurang dari 0.',
|
||||
password: 'Kata Sandi',
|
||||
passwordDescription:
|
||||
@ -1016,4 +1018,3 @@ export default {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { useShowDeleteConfirm, useTranslate } from '@/hooks/common-hooks';
|
||||
import { useRemoveNextDocument } from '@/hooks/document-hooks';
|
||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||
import { api_host } from '@/utils/api';
|
||||
import { downloadFile } from '@/utils/file-util';
|
||||
import { downloadDocument } from '@/utils/file-util';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownloadOutlined,
|
||||
@ -40,8 +39,8 @@ const ParsingActionCell = ({
|
||||
};
|
||||
|
||||
const onDownloadDocument = () => {
|
||||
downloadFile({
|
||||
url: `${api_host}/document/get/${documentId}`,
|
||||
downloadDocument({
|
||||
id: documentId,
|
||||
filename: record.name,
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Authorization } from '@/constants/authorization';
|
||||
import { getAuthorization } from '@/utils/authorization-util';
|
||||
import jsPreviewExcel from '@js-preview/excel';
|
||||
import axios from 'axios';
|
||||
import mammoth from 'mammoth';
|
||||
@ -23,7 +25,12 @@ export const useCatchError = (api: string) => {
|
||||
|
||||
export const useFetchDocument = () => {
|
||||
const fetchDocument = useCallback(async (api: string) => {
|
||||
const ret = await axios.get(api, { responseType: 'arraybuffer' });
|
||||
const ret = await axios.get(api, {
|
||||
headers: {
|
||||
[Authorization]: getAuthorization(),
|
||||
},
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
return ret;
|
||||
}, []);
|
||||
|
||||
@ -64,11 +71,12 @@ export const useFetchExcel = (filePath: string) => {
|
||||
|
||||
export const useFetchDocx = (filePath: string) => {
|
||||
const [succeed, setSucceed] = useState(true);
|
||||
const [error, setError] = useState<string>();
|
||||
const { fetchDocument } = useFetchDocument();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const { error } = useCatchError(filePath);
|
||||
|
||||
const fetchDocumentAsync = useCallback(async () => {
|
||||
try {
|
||||
const jsonFile = await fetchDocument(filePath);
|
||||
mammoth
|
||||
.convertToHtml(
|
||||
@ -88,6 +96,9 @@ export const useFetchDocx = (filePath: string) => {
|
||||
.catch(() => {
|
||||
setSucceed(false);
|
||||
});
|
||||
} catch (error: any) {
|
||||
setError(error.toString());
|
||||
}
|
||||
}, [filePath, fetchDocument]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
import { Authorization } from '@/constants/authorization';
|
||||
import { getAuthorization } from '@/utils/authorization-util';
|
||||
import { Skeleton } from 'antd';
|
||||
import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter';
|
||||
import FileError from '../file-error';
|
||||
import { useCatchError } from '../hooks';
|
||||
type PdfLoaderProps = React.ComponentProps<typeof PdfLoader> & {
|
||||
httpHeaders?: Record<string, string>;
|
||||
};
|
||||
|
||||
const Loader = PdfLoader as React.ComponentType<PdfLoaderProps>;
|
||||
|
||||
interface IProps {
|
||||
url: string;
|
||||
@ -10,11 +17,14 @@ interface IProps {
|
||||
const PdfPreviewer = ({ url }: IProps) => {
|
||||
const { error } = useCatchError(url);
|
||||
const resetHash = () => {};
|
||||
|
||||
const httpHeaders = {
|
||||
[Authorization]: getAuthorization(),
|
||||
};
|
||||
return (
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<PdfLoader
|
||||
<Loader
|
||||
url={url}
|
||||
httpHeaders={httpHeaders}
|
||||
beforeLoad={<Skeleton active />}
|
||||
workerSrc="/pdfjs-dist/pdf.worker.min.js"
|
||||
errorMessage={<FileError>{error}</FileError>}
|
||||
@ -37,7 +47,7 @@ const PdfPreviewer = ({ url }: IProps) => {
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</PdfLoader>
|
||||
</Loader>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import NewDocumentLink from '@/components/new-document-link';
|
||||
import SvgIcon from '@/components/svg-icon';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useDownloadFile } from '@/hooks/file-manager-hooks';
|
||||
import { IFile } from '@/interfaces/database/file-manager';
|
||||
import { api_host } from '@/utils/api';
|
||||
import {
|
||||
getExtension,
|
||||
isSupportedPreviewDocumentType,
|
||||
} from '@/utils/document-util';
|
||||
import { downloadFile } from '@/utils/file-util';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownloadOutlined,
|
||||
@ -42,12 +41,13 @@ const ActionCell = ({
|
||||
[documentId],
|
||||
setSelectedRowKeys,
|
||||
);
|
||||
const { downloadFile, loading } = useDownloadFile();
|
||||
const extension = getExtension(record.name);
|
||||
const isKnowledgeBase = record.source_type === 'knowledgebase';
|
||||
|
||||
const onDownloadDocument = () => {
|
||||
downloadFile({
|
||||
url: `${api_host}/file/get/${documentId}`,
|
||||
id: documentId,
|
||||
filename: record.name,
|
||||
});
|
||||
};
|
||||
@ -106,7 +106,12 @@ const ActionCell = ({
|
||||
)}
|
||||
{record.type !== 'folder' && (
|
||||
<Tooltip title={t('download', { keyPrefix: 'common' })}>
|
||||
<Button type="text" disabled={beingUsed} onClick={onDownloadDocument}>
|
||||
<Button
|
||||
type="text"
|
||||
disabled={beingUsed}
|
||||
loading={loading}
|
||||
onClick={onDownloadDocument}
|
||||
>
|
||||
<DownloadOutlined size={20} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Flex, Form, Input, Modal, Select, Space, InputNumber } from 'antd';
|
||||
import { Flex, Form, Input, Modal, Select, Space } from 'antd';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
type FieldType = IAddLlmRequestBody & {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Form, Input, Modal, Select, Switch, InputNumber } from 'antd';
|
||||
import { Form, Input, InputNumber, Modal, Select, Switch } from 'antd';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
type FieldType = IAddLlmRequestBody & {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Flex, Form, Input, Modal, Select, Space, InputNumber } from 'antd';
|
||||
import { Flex, Form, Input, InputNumber, Modal, Select, Space } from 'antd';
|
||||
import { useMemo } from 'react';
|
||||
import { BedrockRegionList } from '../constant';
|
||||
|
||||
@ -136,7 +136,6 @@ const BedrockModal = ({
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Flex, Form, Input, Modal, Select, Space, InputNumber } from 'antd';
|
||||
import { Flex, Form, Input, InputNumber, Modal, Select, Space } from 'antd';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
type FieldType = IAddLlmRequestBody & {
|
||||
@ -118,7 +118,6 @@ const FishAudioModal = ({
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Form, Input, Modal, Select, InputNumber } from 'antd';
|
||||
import { Form, Input, InputNumber, Modal, Select } from 'antd';
|
||||
|
||||
type FieldType = IAddLlmRequestBody & {
|
||||
google_project_id: string;
|
||||
@ -112,7 +112,6 @@ const GoogleModal = ({
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@ -1,7 +1,16 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Flex, Form, Input, Modal, Select, Space, Switch, InputNumber } from 'antd';
|
||||
import {
|
||||
Flex,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
} from 'antd';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
type FieldType = IAddLlmRequestBody & { vision: boolean };
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Form, Input, Modal, Select, InputNumber } from 'antd';
|
||||
import { Form, Input, InputNumber, Modal, Select } from 'antd';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
type FieldType = IAddLlmRequestBody & {
|
||||
@ -83,7 +83,9 @@ const SparkModal = ({
|
||||
<Form.Item<FieldType>
|
||||
label={t('addSparkAPIPassword')}
|
||||
name="spark_api_password"
|
||||
rules={[{ required: true, message: t('SparkAPIPasswordMessage') }]}
|
||||
rules={[
|
||||
{ required: true, message: t('SparkAPIPasswordMessage') },
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('SparkAPIPasswordMessage')} />
|
||||
</Form.Item>
|
||||
@ -109,7 +111,9 @@ const SparkModal = ({
|
||||
<Form.Item<FieldType>
|
||||
label={t('addSparkAPISecret')}
|
||||
name="spark_api_secret"
|
||||
rules={[{ required: true, message: t('SparkAPISecretMessage') }]}
|
||||
rules={[
|
||||
{ required: true, message: t('SparkAPISecretMessage') },
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('SparkAPISecretMessage')} />
|
||||
</Form.Item>
|
||||
@ -153,7 +157,6 @@ const SparkModal = ({
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Flex, Form, Input, Modal, Select, Space, Switch, InputNumber } from 'antd';
|
||||
import { Flex, Form, Input, InputNumber, Modal, Select, Space } from 'antd';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
type FieldType = IAddLlmRequestBody & {
|
||||
@ -128,7 +128,6 @@ const VolcEngineModal = ({
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { Form, Input, Modal, Select, InputNumber } from 'antd';
|
||||
import { Form, Input, InputNumber, Modal, Select } from 'antd';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
type FieldType = IAddLlmRequestBody & {
|
||||
|
||||
@ -45,11 +45,16 @@ const methods = {
|
||||
url: connectFileToKnowledge,
|
||||
method: 'post',
|
||||
},
|
||||
getDocumentFile: {
|
||||
getFile: {
|
||||
url: getFile,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
},
|
||||
getDocumentFile: {
|
||||
url: get_document_file,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
},
|
||||
moveFile: {
|
||||
url: moveFile,
|
||||
method: 'post',
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import fileManagerService from '@/services/file-manager-service';
|
||||
import { UploadFile } from 'antd';
|
||||
|
||||
export const transformFile2Base64 = (val: any): Promise<any> => {
|
||||
@ -100,29 +101,41 @@ export const getBase64FromUploadFileList = async (fileList?: UploadFile[]) => {
|
||||
return '';
|
||||
};
|
||||
|
||||
export const downloadFile = ({
|
||||
url,
|
||||
export const downloadFileFromBlob = (blob: Blob, name?: string) => {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
if (name) {
|
||||
a.download = name;
|
||||
}
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
export const downloadFile = async ({
|
||||
id,
|
||||
filename,
|
||||
target,
|
||||
}: {
|
||||
url: string;
|
||||
id: string;
|
||||
filename?: string;
|
||||
target?: string;
|
||||
}) => {
|
||||
console.log('downloadFile', url);
|
||||
const downloadElement = document.createElement('a');
|
||||
downloadElement.style.display = 'none';
|
||||
downloadElement.href = url;
|
||||
if (target) {
|
||||
downloadElement.target = '_blank';
|
||||
}
|
||||
downloadElement.rel = 'noopener noreferrer';
|
||||
if (filename) {
|
||||
downloadElement.download = filename;
|
||||
}
|
||||
document.body.appendChild(downloadElement);
|
||||
downloadElement.click();
|
||||
document.body.removeChild(downloadElement);
|
||||
const response = await fileManagerService.getFile({}, id);
|
||||
const blob = new Blob([response.data], { type: response.data.type });
|
||||
downloadFileFromBlob(blob, filename);
|
||||
};
|
||||
|
||||
export const downloadDocument = async ({
|
||||
id,
|
||||
filename,
|
||||
}: {
|
||||
id: string;
|
||||
filename?: string;
|
||||
}) => {
|
||||
const response = await fileManagerService.getDocumentFile({}, id);
|
||||
const blob = new Blob([response.data], { type: response.data.type });
|
||||
downloadFileFromBlob(blob, filename);
|
||||
};
|
||||
|
||||
const Units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user