diff --git a/src/App.tsx b/src/App.tsx
index 2bf0a6f..592f2de 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -19,6 +19,7 @@ import ToastContainer from "./components/toast/ToastContainer";
import { toast } from "./components/toast/toastStore";
import { flushPendingGenerationRecords } from "./api/generationRecordClient";
import { keyServerClient } from "./api/keyServerClient";
+import { preloadPublicConfig, getLogoUrl } from "./api/publicConfigClient";
import { setUserMaxConcurrency } from "./api/generationConcurrency";
import {
SERVER_SESSION_EXPIRED_EVENT,
@@ -242,6 +243,8 @@ function App() {
let cancelled = false;
const loadSession = async () => {
+ // 预加载公网配置(OSS base / logo URL),与 session 加载并行,不阻断启动。
+ void preloadPublicConfig();
try {
const nextSession = await keyServerClient.getCurrentSession();
if (cancelled) return;
@@ -414,7 +417,7 @@ function App() {
-
+
{authMode === "login" ? "欢迎回来" : "创建账号"}
{authMode === "login" ? "登录后继续你的 AI 创作之旅" : "注册即可免费体验全部功能"}
diff --git a/src/api/publicConfigClient.ts b/src/api/publicConfigClient.ts
new file mode 100644
index 0000000..5e80860
--- /dev/null
+++ b/src/api/publicConfigClient.ts
@@ -0,0 +1,65 @@
+// 前端公网配置客户端。
+// 从 GET /api/public/config/profile?name=web-public-config 拉取运行时配置,
+// 包括 OSS 公网 base URL 与 logo URL。
+// 按 AGENTS.md 规则 1/4/5:这些环境权威数据不硬编码在前端源码,由 API 下发。
+//
+// 设计:进程内单例缓存 + promise 去重,App 启动时预加载一次,
+// 之后 getOssPublicBaseUrl() / getLogoUrl() 同步返回缓存值。
+// API 不可用时回退到 FALLBACK 值(当前生产 bucket),保证渐进可用。
+
+import { serverRequest } from "./serverConnection";
+
+interface PublicConfigPayload {
+ name: string;
+ config: {
+ ossPublicBaseUrl?: string;
+ logoUrl?: string;
+ };
+ description?: string;
+ updatedAt?: string;
+}
+
+// Fallback:API 不可用或未加载时的兜底值,保证首屏不白屏。
+// 这些值仅作为降级,正式来源是 API 返回的 config。
+const FALLBACK_OSS_PUBLIC_BASE_URL = "https://stringtest.oss-cn-hangzhou.aliyuncs.com";
+const FALLBACK_LOGO_URL = "https://stringtest.oss-cn-hangzhou.aliyuncs.com/muban/logo.png";
+
+let cachedConfig: PublicConfigPayload["config"] | null = null;
+let loadPromise: Promise | null = null;
+
+async function fetchPublicConfig(): Promise {
+ const payload = await serverRequest("public/config/profile?name=web-public-config", {
+ // 公开端点,无需 token。
+ maxRetries: 2,
+ fallbackMessage: "加载公网配置失败",
+ });
+ return payload?.config ?? {};
+}
+
+/** 预加载公网配置。App 启动时调用一次,后续同步读取缓存。可安全重复调用(promise 去重)。 */
+export async function preloadPublicConfig(): Promise {
+ if (loadPromise) return loadPromise.then(() => undefined);
+ loadPromise = fetchPublicConfig()
+ .then((config) => {
+ cachedConfig = config;
+ return config;
+ })
+ .catch((error) => {
+ // 加载失败不阻断启动,用 fallback 值;记录后允许后续重试。
+ console.warn("[publicConfig] 加载失败,使用 fallback 值", error);
+ cachedConfig = null;
+ loadPromise = null;
+ return {};
+ });
+ return loadPromise.then(() => undefined);
+}
+
+/** 同步获取 OSS 公网 base URL。未加载时返回 fallback。 */
+export function getOssPublicBaseUrl(): string {
+ return cachedConfig?.ossPublicBaseUrl?.trim() || FALLBACK_OSS_PUBLIC_BASE_URL;
+}
+
+/** 同步获取 logo URL。未加载时返回 fallback。 */
+export function getLogoUrl(): string {
+ return cachedConfig?.logoUrl?.trim() || FALLBACK_LOGO_URL;
+}
diff --git a/src/components/Topbar.tsx b/src/components/Topbar.tsx
index 9bba4af..1eee5ad 100644
--- a/src/components/Topbar.tsx
+++ b/src/components/Topbar.tsx
@@ -10,6 +10,7 @@ import {
WalletOutlined,
} from "@ant-design/icons";
import { LocalAvatar } from "./LocalAvatar";
+import { getLogoUrl } from "../api/publicConfigClient";
import type { WebUserSession } from "../types";
interface TopbarProps {
@@ -110,7 +111,7 @@ export function Topbar({
onClick={onOpenWorkspace}
>
-
+
OmniAI 电商智能体
diff --git a/src/data/ossAssets.ts b/src/data/ossAssets.ts
index 5065378..75d2d0b 100644
--- a/src/data/ossAssets.ts
+++ b/src/data/ossAssets.ts
@@ -1,7 +1,10 @@
-const OSS_PUBLIC_BASE_URL = "https://stringtest.oss-cn-hangzhou.aliyuncs.com";
+// OSS 公网 base URL 由 API 下发(AGENTS.md 规则 1/5),
+// 见 src/api/publicConfigClient.ts。ossAssets 在模块加载时同步取缓存值,
+// App 启动时 preloadPublicConfig() 已预加载;未加载时 getOssPublicBaseUrl() 返回 fallback。
+import { getOssPublicBaseUrl } from "../api/publicConfigClient";
function oss(path: string): string {
- return `${OSS_PUBLIC_BASE_URL}/${path.replace(/^\/+/, "")}`;
+ return `${getOssPublicBaseUrl()}/${path.replace(/^\/+/, "")}`;
}
function muban(path: string): string {