mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 15:31:05 +08:00
fix(preview): authenticate markdown document preview requests (#15589)
## Summary Fixes [#15585](https://github.com/infiniflow/ragflow/issues/15585). - Route markdown preview through the shared `request` client (same as txt/image previewers) so `Authorization` headers and interceptors are applied consistently. - Add a unit test covering `AUTH_BETA` token loading for embedded search auth. ## Root cause Search result preview for `.md`/`.mdx` used raw `fetch`, which did not apply the same auth path as other preview types. That led to `401` on `GET /api/v1/documents/{id}/preview` even when the user was logged in or using an embedded search `auth` query param. ## Test plan - [ ] Log in, run a search, open a markdown citation link — preview loads (no 401). - [ ] Open an embedded shared search URL with `auth` query param, preview a markdown file — preview loads. - [ ] Confirm PDF/txt preview still works in the same search UI. --------- Co-authored-by: MkDev11 <89318445+bitloi@users.noreply.github.com> Co-authored-by: Wang Qi <wangq8@outlook.com>
This commit is contained in:
@@ -156,6 +156,12 @@ def test_load_user_api_token_fallback_and_fallback_exception(monkeypatch, caplog
|
||||
monkeypatch.setattr(apps_module.Serializer, "loads", _raise_decode)
|
||||
|
||||
fallback_user_empty_token = SimpleNamespace(email="fallback@example.com", access_token="")
|
||||
valid_token = "a" * 32
|
||||
beta_user = SimpleNamespace(
|
||||
id="tenant-1",
|
||||
email="embed@example.com",
|
||||
access_token=valid_token,
|
||||
)
|
||||
|
||||
async def _case():
|
||||
monkeypatch.setattr(apps_module.APIToken, "query", lambda **_kwargs: [SimpleNamespace(tenant_id="tenant-1")])
|
||||
@@ -171,6 +177,29 @@ def test_load_user_api_token_fallback_and_fallback_exception(monkeypatch, caplog
|
||||
with caplog.at_level(logging.WARNING):
|
||||
assert apps_module._load_user() is None
|
||||
|
||||
def _query_api_token(**kwargs):
|
||||
if kwargs.get("beta") == "embed-beta":
|
||||
return [SimpleNamespace(tenant_id="tenant-1")]
|
||||
return []
|
||||
|
||||
def _query_user(**kwargs):
|
||||
if (
|
||||
kwargs.get("id") == "tenant-1"
|
||||
and kwargs.get("status") == apps_module.StatusEnum.VALID.value
|
||||
):
|
||||
return [beta_user]
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(apps_module.APIToken, "query", _query_api_token)
|
||||
monkeypatch.setattr(apps_module.UserService, "query", _query_user)
|
||||
async with quart_app.test_request_context("/", headers={"Authorization": "Bearer embed-beta"}):
|
||||
user = apps_module._load_user(auth_types=[apps_module.AUTH_BETA])
|
||||
assert user is beta_user
|
||||
assert apps_module.g.auth_type == apps_module.AUTH_BETA
|
||||
|
||||
async with quart_app.test_request_context("/", headers={"Authorization": "Bearer invalid-beta"}):
|
||||
assert apps_module._load_user(auth_types=[apps_module.AUTH_BETA]) is None
|
||||
|
||||
_run(_case())
|
||||
assert "api token fallback failed" in caplog.text
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Authorization } from '@/constants/authorization';
|
||||
import message from '@/components/ui/message';
|
||||
import { Spin } from '@/components/ui/spin';
|
||||
import { MarkdownRemarkPluginsLite } from '@/constants/markdown-remark-plugins';
|
||||
import { cn } from '@/lib/utils';
|
||||
import FileError from '@/pages/document-viewer/file-error';
|
||||
import { getAuthorization } from '@/utils/authorization-util';
|
||||
import request from '@/utils/request';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
|
||||
@@ -15,16 +16,52 @@ interface MdProps {
|
||||
export const Md: React.FC<MdProps> = ({ url, className }) => {
|
||||
const [content, setContent] = useState<string>('');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!url) {
|
||||
setContent('');
|
||||
setError(null);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setError(null);
|
||||
fetch(url, { headers: { [Authorization]: getAuthorization() } })
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error('Failed to fetch markdown file');
|
||||
return res.text();
|
||||
})
|
||||
.then((text) => setContent(text))
|
||||
.catch((err) => setError(err.message));
|
||||
setLoading(true);
|
||||
|
||||
const fetchMarkdown = async () => {
|
||||
try {
|
||||
const res = await request(url, {
|
||||
method: 'GET',
|
||||
responseType: 'blob',
|
||||
onError: (err: unknown) => {
|
||||
console.error('Error loading markdown file:', err);
|
||||
},
|
||||
});
|
||||
if (cancelled) return;
|
||||
|
||||
const blob = res.data;
|
||||
const text = await blob.text();
|
||||
if (cancelled) return;
|
||||
setContent(text);
|
||||
} catch (err: unknown) {
|
||||
if (cancelled) return;
|
||||
const messageText =
|
||||
err instanceof Error ? err.message : 'Failed to fetch markdown file';
|
||||
setError(messageText);
|
||||
message.error('Failed to load file');
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchMarkdown();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
if (error) return <FileError>{error}</FileError>;
|
||||
@@ -32,11 +69,21 @@ export const Md: React.FC<MdProps> = ({ url, className }) => {
|
||||
return (
|
||||
<div
|
||||
style={{ padding: 4, overflow: 'scroll' }}
|
||||
className={cn(className, 'markdown-body h-[calc(100vh - 200px)]')}
|
||||
className={cn(
|
||||
className,
|
||||
'markdown-body relative h-[calc(100vh - 200px)]',
|
||||
)}
|
||||
>
|
||||
<ReactMarkdown remarkPlugins={MarkdownRemarkPluginsLite}>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
{loading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<Spin />
|
||||
</div>
|
||||
)}
|
||||
{!loading && (
|
||||
<ReactMarkdown remarkPlugins={MarkdownRemarkPluginsLite}>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user