diff --git a/test/testcases/test_web_api/test_system_app/test_apps_init_unit.py b/test/testcases/test_web_api/test_system_app/test_apps_init_unit.py index c7d951270a..0ba7ee463a 100644 --- a/test/testcases/test_web_api/test_system_app/test_apps_init_unit.py +++ b/test/testcases/test_web_api/test_system_app/test_apps_init_unit.py @@ -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 diff --git a/web/src/components/document-preview/md/index.tsx b/web/src/components/document-preview/md/index.tsx index 13f1af3c2f..c4c4943e15 100644 --- a/web/src/components/document-preview/md/index.tsx +++ b/web/src/components/document-preview/md/index.tsx @@ -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 = ({ url, className }) => { const [content, setContent] = useState(''); const [error, setError] = useState(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 {error}; @@ -32,11 +69,21 @@ export const Md: React.FC = ({ url, className }) => { return (
- - {content} - + {loading && ( +
+ +
+ )} + {!loading && ( + + {content} + + )}
); };