mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
fix: finalize v1.21.0 release workflow
This commit is contained in:
@@ -330,9 +330,6 @@ jobs:
|
||||
|
||||
with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as env_file:
|
||||
env_file.write(f"TAURI_SIGNING_PRIVATE_KEY_PATH={key_path}\n")
|
||||
env_file.write("TAURI_SIGNING_PRIVATE_KEY<<__TAURI_SIGNING_PRIVATE_KEY__\n")
|
||||
env_file.write(normalized_key.rstrip("\n") + "\n")
|
||||
env_file.write("__TAURI_SIGNING_PRIVATE_KEY__\n")
|
||||
|
||||
print(f"Normalized updater key written to {key_path}")
|
||||
PY
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { act } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { RootRouter } from "./RootRouter";
|
||||
|
||||
const {
|
||||
mockFinalizeModuleImportAutoReload,
|
||||
mockGetRuntimeAppVersion,
|
||||
mockStartOemCloudStartupLoginIfRequired,
|
||||
} = vi.hoisted(() => ({
|
||||
mockFinalizeModuleImportAutoReload: vi.fn(),
|
||||
mockGetRuntimeAppVersion: vi.fn(() => "0.0.0-test"),
|
||||
mockStartOemCloudStartupLoginIfRequired: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./App", () => ({
|
||||
default: () => <div data-testid="main-app">主应用</div>,
|
||||
}));
|
||||
|
||||
vi.mock("./pages/smart-input", () => ({
|
||||
SmartInputPage: () => <div data-testid="smart-input-page" />,
|
||||
}));
|
||||
|
||||
vi.mock("./pages/update-notification", () => ({
|
||||
UpdateNotificationPage: () => <div data-testid="update-notification-page" />,
|
||||
}));
|
||||
|
||||
vi.mock("./pages", () => ({
|
||||
BrowserRuntimeDebuggerPage: () => (
|
||||
<div data-testid="browser-runtime-debugger-page" />
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./features/resource-manager", () => ({
|
||||
ResourceManagerPage: () => <div data-testid="resource-manager-page" />,
|
||||
}));
|
||||
|
||||
vi.mock("./components/settings-v2/system/chrome-relay/guide-window", () => ({
|
||||
BrowserConnectorGuideWindow: () => (
|
||||
<div data-testid="browser-connector-guide-page" />
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./components/ui/sonner", () => ({
|
||||
Toaster: () => <div data-testid="toaster" />,
|
||||
}));
|
||||
|
||||
vi.mock("./components/layout/AppCrashBoundary", () => ({
|
||||
AppCrashBoundary: ({ children }: { children: ReactNode }) => (
|
||||
<>{children}</>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./components/layout/CrashRecoveryPanel.helpers", () => ({
|
||||
finalizeModuleImportAutoReload: mockFinalizeModuleImportAutoReload,
|
||||
}));
|
||||
|
||||
vi.mock("./lib/appVersion", () => ({
|
||||
getRuntimeAppVersion: mockGetRuntimeAppVersion,
|
||||
}));
|
||||
|
||||
vi.mock("./lib/oemCloudStartupLogin", () => ({
|
||||
startOemCloudStartupLoginIfRequired:
|
||||
mockStartOemCloudStartupLoginIfRequired,
|
||||
}));
|
||||
|
||||
interface MountedRootRouter {
|
||||
container: HTMLDivElement;
|
||||
root: Root;
|
||||
}
|
||||
|
||||
const mounted: MountedRootRouter[] = [];
|
||||
|
||||
async function renderRootRouter(pathname: string) {
|
||||
window.history.pushState({}, "", pathname);
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
|
||||
await act(async () => {
|
||||
root.render(<RootRouter />);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const page = { container, root };
|
||||
mounted.push(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
describe("RootRouter", () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
||||
mockStartOemCloudStartupLoginIfRequired.mockResolvedValue({
|
||||
status: "not_configured",
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const item of mounted.splice(0)) {
|
||||
act(() => item.root.unmount());
|
||||
item.container.remove();
|
||||
}
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("打开主应用时应触发启动期云端登录判断", async () => {
|
||||
const { container } = await renderRootRouter("/");
|
||||
|
||||
expect(container.textContent).toContain("主应用");
|
||||
expect(mockStartOemCloudStartupLoginIfRequired).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("独立工具窗口不应触发主应用登录流程", async () => {
|
||||
const { container } = await renderRootRouter("/smart-input");
|
||||
|
||||
expect(
|
||||
container.querySelector('[data-testid="smart-input-page"]'),
|
||||
).not.toBeNull();
|
||||
expect(mockStartOemCloudStartupLoginIfRequired).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -14,6 +14,7 @@ import { Toaster } from "./components/ui/sonner";
|
||||
import { AppCrashBoundary } from "./components/layout/AppCrashBoundary";
|
||||
import { finalizeModuleImportAutoReload } from "./components/layout/CrashRecoveryPanel.helpers";
|
||||
import { getRuntimeAppVersion } from "./lib/appVersion";
|
||||
import { startOemCloudStartupLoginIfRequired } from "./lib/oemCloudStartupLogin";
|
||||
|
||||
/**
|
||||
* 根据 URL 路径渲染对应的组件
|
||||
@@ -27,6 +28,13 @@ import { getRuntimeAppVersion } from "./lib/appVersion";
|
||||
*/
|
||||
export function RootRouter() {
|
||||
const pathname = window.location.pathname;
|
||||
const isMainAppRoute = ![
|
||||
"/smart-input",
|
||||
"/update-notification",
|
||||
"/browser-runtime-debugger",
|
||||
"/resource-manager",
|
||||
"/browser-connector-guide",
|
||||
].includes(pathname);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") {
|
||||
@@ -41,6 +49,14 @@ export function RootRouter() {
|
||||
);
|
||||
}, [pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMainAppRoute) {
|
||||
return;
|
||||
}
|
||||
|
||||
void startOemCloudStartupLoginIfRequired();
|
||||
}, [isMainAppRoute]);
|
||||
|
||||
// 截图对话悬浮窗口路由(也用于语音输入)
|
||||
if (pathname === "/smart-input") {
|
||||
return (
|
||||
|
||||
@@ -285,6 +285,7 @@ describe("WebSearchSettings", () => {
|
||||
);
|
||||
await leaveTip(heroTip);
|
||||
|
||||
await switchTab(document.body, "图片搜索");
|
||||
const pexelsTip = await hoverTip("Pexels 接入说明");
|
||||
expect(getBodyText()).toContain(
|
||||
"申请地址:https://www.pexels.com/api/new/",
|
||||
@@ -312,6 +313,8 @@ describe("WebSearchSettings", () => {
|
||||
findInput(container, "web-search-provider-priority"),
|
||||
"multi_search_engine, tavily, bing_search_api",
|
||||
);
|
||||
|
||||
await switchTab(container, "Provider 凭证");
|
||||
await setInputValue(
|
||||
findInput(container, "web-search-tavily-key"),
|
||||
"tavily-new-key",
|
||||
@@ -328,6 +331,8 @@ describe("WebSearchSettings", () => {
|
||||
findInput(container, "web-search-google-engine-id"),
|
||||
"cx-new-id",
|
||||
);
|
||||
|
||||
await switchTab(container, "MSE 聚合");
|
||||
await setInputValue(
|
||||
findInput(container, "web-search-mse-custom-engine-name"),
|
||||
"hn",
|
||||
@@ -336,6 +341,8 @@ describe("WebSearchSettings", () => {
|
||||
findInput(container, "web-search-mse-custom-engine-template"),
|
||||
"https://hn.algolia.com/?q={query}",
|
||||
);
|
||||
|
||||
await switchTab(container, "图片搜索");
|
||||
await setInputValue(
|
||||
findInput(container, "web-search-pexels-key"),
|
||||
"new-key",
|
||||
@@ -384,6 +391,7 @@ describe("WebSearchSettings", () => {
|
||||
await flushEffects();
|
||||
await flushEffects();
|
||||
|
||||
await switchTab(container, "图片搜索");
|
||||
await act(async () => {
|
||||
findButton(container, "申请 Pexels Key").click();
|
||||
await flushEffects();
|
||||
@@ -397,6 +405,7 @@ describe("WebSearchSettings", () => {
|
||||
await flushEffects();
|
||||
await flushEffects();
|
||||
|
||||
await switchTab(container, "Provider 凭证");
|
||||
await act(async () => {
|
||||
findButton(container, "申请 Tavily Key").click();
|
||||
await flushEffects();
|
||||
@@ -417,6 +426,7 @@ describe("WebSearchSettings", () => {
|
||||
await flushEffects();
|
||||
await flushEffects();
|
||||
|
||||
await switchTab(container, "图片搜索");
|
||||
await act(async () => {
|
||||
findButton(container, "申请 Pexels Key").click();
|
||||
await flushEffects();
|
||||
@@ -436,6 +446,7 @@ describe("WebSearchSettings", () => {
|
||||
await flushEffects();
|
||||
await flushEffects();
|
||||
|
||||
await switchTab(container, "图片搜索");
|
||||
await act(async () => {
|
||||
findButton(container, "申请 Pixabay Key").click();
|
||||
await flushEffects();
|
||||
@@ -451,6 +462,7 @@ describe("WebSearchSettings", () => {
|
||||
await flushEffects();
|
||||
await flushEffects();
|
||||
|
||||
await switchTab(container, "Provider 凭证");
|
||||
await act(async () => {
|
||||
findButton(container, "申请 Bing Key").click();
|
||||
await flushEffects();
|
||||
@@ -466,6 +478,7 @@ describe("WebSearchSettings", () => {
|
||||
await flushEffects();
|
||||
await flushEffects();
|
||||
|
||||
await switchTab(container, "Provider 凭证");
|
||||
await act(async () => {
|
||||
findButton(container, "申请 Google Key").click();
|
||||
await flushEffects();
|
||||
@@ -481,6 +494,7 @@ describe("WebSearchSettings", () => {
|
||||
await flushEffects();
|
||||
await flushEffects();
|
||||
|
||||
await switchTab(container, "Provider 凭证");
|
||||
await act(async () => {
|
||||
findButton(container, "创建 CSE").click();
|
||||
await flushEffects();
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
getClientCreditsDashboard,
|
||||
getClientProviderOffer,
|
||||
getClientReferralDashboard,
|
||||
listPublicOAuthProviders,
|
||||
listClientPaymentConfigs,
|
||||
listClientPlans,
|
||||
listClientProviderOfferModels,
|
||||
@@ -154,6 +155,54 @@ describe("oemCloudControlPlane desktop auth", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("应读取公开 OAuth Provider 目录供启动登录判断使用", async () => {
|
||||
const fetchMock = vi.fn(async () => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => ({
|
||||
code: 200,
|
||||
message: "success",
|
||||
data: {
|
||||
items: [
|
||||
{
|
||||
provider: "google",
|
||||
displayName: "Google",
|
||||
authorizeUrl: "https://user.limeai.run/oauth/google",
|
||||
redirectUri: "https://user.limeai.run/oauth/callback",
|
||||
scopes: ["openid", "email"],
|
||||
enabled: true,
|
||||
loginHint: "使用 Google 登录",
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
}));
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const providers = await listPublicOAuthProviders("tenant-0001");
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
"https://user.limeai.run/api/v1/public/tenants/tenant-0001/oauth/providers",
|
||||
expect.objectContaining({
|
||||
method: "GET",
|
||||
headers: expect.objectContaining({
|
||||
Accept: "application/json",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(providers).toEqual([
|
||||
{
|
||||
provider: "google",
|
||||
displayName: "Google",
|
||||
authorizeUrl: "https://user.limeai.run/oauth/google",
|
||||
redirectUri: "https://user.limeai.run/oauth/callback",
|
||||
scopes: ["openid", "email"],
|
||||
enabled: true,
|
||||
loginHint: "使用 Google 登录",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("应解析 bootstrap 中缓存的邀请开关与分享事实源", async () => {
|
||||
window.__LIME_SESSION_TOKEN__ = "session-token-001";
|
||||
|
||||
|
||||
@@ -47,6 +47,16 @@ export interface OemCloudUserSession {
|
||||
expiresAt: string;
|
||||
}
|
||||
|
||||
export interface OemCloudPublicOAuthProvider {
|
||||
provider: string;
|
||||
displayName: string;
|
||||
authorizeUrl?: string;
|
||||
redirectUri?: string;
|
||||
scopes: string[];
|
||||
enabled: boolean;
|
||||
loginHint?: string;
|
||||
}
|
||||
|
||||
export interface OemCloudCurrentSession extends Omit<
|
||||
OemCloudCurrentSessionLike,
|
||||
"tenant" | "user" | "session"
|
||||
@@ -1096,6 +1106,28 @@ function parseCurrentSession(value: unknown): OemCloudCurrentSession {
|
||||
};
|
||||
}
|
||||
|
||||
function parsePublicOAuthProvider(value: unknown): OemCloudPublicOAuthProvider {
|
||||
if (!isRecord(value)) {
|
||||
throw new OemCloudControlPlaneError("OAuth Provider 格式非法");
|
||||
}
|
||||
|
||||
const provider = normalizeText(value.provider);
|
||||
const displayName = normalizeText(value.displayName) ?? provider;
|
||||
if (!provider || !displayName) {
|
||||
throw new OemCloudControlPlaneError("OAuth Provider 格式非法");
|
||||
}
|
||||
|
||||
return {
|
||||
provider,
|
||||
displayName,
|
||||
authorizeUrl: normalizeText(value.authorizeUrl) ?? undefined,
|
||||
redirectUri: normalizeText(value.redirectUri) ?? undefined,
|
||||
scopes: normalizeStringArray(value.scopes),
|
||||
enabled: normalizeBoolean(value.enabled, true),
|
||||
loginHint: normalizeText(value.loginHint) ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function parseProviderOfferSummary(
|
||||
value: unknown,
|
||||
): OemCloudProviderOfferSummary {
|
||||
@@ -2402,6 +2434,18 @@ export async function pollClientDesktopAuthSession(
|
||||
);
|
||||
}
|
||||
|
||||
export async function listPublicOAuthProviders(
|
||||
tenantId: string,
|
||||
): Promise<OemCloudPublicOAuthProvider[]> {
|
||||
const response = await requestControlPlane<{ items?: unknown[] }>(
|
||||
`/v1/public/tenants/${encodeURIComponent(tenantId)}/oauth/providers`,
|
||||
);
|
||||
|
||||
return Array.isArray(response.items)
|
||||
? response.items.map(parsePublicOAuthProvider)
|
||||
: [];
|
||||
}
|
||||
|
||||
export async function verifyClientAuthEmailCode(
|
||||
tenantId: string,
|
||||
payload: VerifyClientAuthEmailCodePayload,
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { setStoredOemCloudSessionState } from "@/lib/oemCloudSession";
|
||||
import { startOemCloudStartupLoginIfRequired } from "@/lib/oemCloudStartupLogin";
|
||||
|
||||
const { mockListPublicOAuthProviders, mockStartOemCloudLogin } = vi.hoisted(
|
||||
() => ({
|
||||
mockListPublicOAuthProviders: vi.fn(),
|
||||
mockStartOemCloudLogin: vi.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock("@/lib/api/oemCloudControlPlane", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("@/lib/api/oemCloudControlPlane")>();
|
||||
|
||||
return {
|
||||
...actual,
|
||||
listPublicOAuthProviders: mockListPublicOAuthProviders,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@/lib/oemCloudLoginLauncher", () => ({
|
||||
startOemCloudLogin: mockStartOemCloudLogin,
|
||||
}));
|
||||
|
||||
function configureRuntime() {
|
||||
window.__LIME_OEM_CLOUD__ = {
|
||||
enabled: true,
|
||||
baseUrl: "https://user.limeai.run",
|
||||
tenantId: "tenant-0001",
|
||||
desktopClientId: "desktop-client",
|
||||
desktopOauthRedirectUrl: "lime://oauth/callback",
|
||||
};
|
||||
}
|
||||
|
||||
describe("oemCloudStartupLogin", () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
delete window.__LIME_BOOTSTRAP__;
|
||||
delete window.__LIME_OEM_CLOUD__;
|
||||
delete window.__LIME_SESSION_TOKEN__;
|
||||
mockListPublicOAuthProviders.mockResolvedValue([
|
||||
{
|
||||
provider: "google",
|
||||
displayName: "Google",
|
||||
enabled: true,
|
||||
scopes: ["openid", "email"],
|
||||
},
|
||||
]);
|
||||
mockStartOemCloudLogin.mockResolvedValue({
|
||||
mode: "desktop_auth",
|
||||
openedUrl: "https://user.limeai.run/oauth/desktop/authorize",
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
delete window.__LIME_BOOTSTRAP__;
|
||||
delete window.__LIME_OEM_CLOUD__;
|
||||
delete window.__LIME_SESSION_TOKEN__;
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("启动时发现品牌云端配置了 Google 登录且本地无会话,应发起登录", async () => {
|
||||
configureRuntime();
|
||||
|
||||
const result = await startOemCloudStartupLoginIfRequired();
|
||||
|
||||
expect(result.status).toBe("started");
|
||||
expect(mockListPublicOAuthProviders).toHaveBeenCalledWith("tenant-0001");
|
||||
expect(mockStartOemCloudLogin).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
baseUrl: "https://user.limeai.run",
|
||||
tenantId: "tenant-0001",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("同一窗口已尝试启动登录后不应重复打开浏览器", async () => {
|
||||
configureRuntime();
|
||||
|
||||
await startOemCloudStartupLoginIfRequired();
|
||||
const result = await startOemCloudStartupLoginIfRequired();
|
||||
|
||||
expect(result.status).toBe("already_attempted");
|
||||
expect(mockStartOemCloudLogin).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("已有当前租户会话时不应启动登录", async () => {
|
||||
configureRuntime();
|
||||
setStoredOemCloudSessionState({
|
||||
token: "session-token",
|
||||
tenant: { id: "tenant-0001" },
|
||||
user: { id: "user-001" },
|
||||
session: { id: "session-001", provider: "google" },
|
||||
});
|
||||
|
||||
const result = await startOemCloudStartupLoginIfRequired();
|
||||
|
||||
expect(result.status).toBe("has_session");
|
||||
expect(mockListPublicOAuthProviders).not.toHaveBeenCalled();
|
||||
expect(mockStartOemCloudLogin).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("后端未下发 Google Provider 时保持开源默认使用", async () => {
|
||||
configureRuntime();
|
||||
mockListPublicOAuthProviders.mockResolvedValue([
|
||||
{
|
||||
provider: "github",
|
||||
displayName: "GitHub",
|
||||
enabled: true,
|
||||
scopes: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await startOemCloudStartupLoginIfRequired();
|
||||
|
||||
expect(result.status).toBe("no_google_provider");
|
||||
expect(mockStartOemCloudLogin).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import {
|
||||
listPublicOAuthProviders,
|
||||
type OemCloudPublicOAuthProvider,
|
||||
} from "@/lib/api/oemCloudControlPlane";
|
||||
import {
|
||||
resolveOemCloudRuntimeContext,
|
||||
type OemCloudRuntimeContext,
|
||||
} from "@/lib/api/oemCloudRuntime";
|
||||
import { getStoredOemCloudSessionState } from "@/lib/oemCloudSession";
|
||||
import { startOemCloudLogin } from "@/lib/oemCloudLoginLauncher";
|
||||
|
||||
const STARTUP_LOGIN_ATTEMPT_PREFIX = "lime:oem-cloud-startup-login:v1";
|
||||
|
||||
export type OemCloudStartupLoginStatus =
|
||||
| "not_configured"
|
||||
| "has_session"
|
||||
| "already_attempted"
|
||||
| "no_google_provider"
|
||||
| "started"
|
||||
| "failed";
|
||||
|
||||
export interface OemCloudStartupLoginResult {
|
||||
status: OemCloudStartupLoginStatus;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
function normalizeProvider(value: string | undefined): string {
|
||||
return value?.trim().toLowerCase() ?? "";
|
||||
}
|
||||
|
||||
function hasGoogleOAuthProvider(
|
||||
providers: OemCloudPublicOAuthProvider[],
|
||||
): boolean {
|
||||
return providers.some(
|
||||
(provider) =>
|
||||
provider.enabled !== false &&
|
||||
normalizeProvider(provider.provider) === "google",
|
||||
);
|
||||
}
|
||||
|
||||
function hasCurrentTenantSession(runtime: OemCloudRuntimeContext): boolean {
|
||||
const storedSession = getStoredOemCloudSessionState();
|
||||
return Boolean(
|
||||
storedSession?.token &&
|
||||
storedSession.session.tenant.id === runtime.tenantId,
|
||||
);
|
||||
}
|
||||
|
||||
function getStartupLoginAttemptKey(runtime: OemCloudRuntimeContext): string {
|
||||
return `${STARTUP_LOGIN_ATTEMPT_PREFIX}:${runtime.tenantId}:${runtime.baseUrl}`;
|
||||
}
|
||||
|
||||
function readStartupAttempt(runtime: OemCloudRuntimeContext): boolean {
|
||||
if (typeof window === "undefined" || !window.sessionStorage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
window.sessionStorage.getItem(getStartupLoginAttemptKey(runtime)) === "1"
|
||||
);
|
||||
}
|
||||
|
||||
function markStartupAttempt(runtime: OemCloudRuntimeContext): void {
|
||||
if (typeof window === "undefined" || !window.sessionStorage) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.sessionStorage.setItem(getStartupLoginAttemptKey(runtime), "1");
|
||||
}
|
||||
|
||||
export async function startOemCloudStartupLoginIfRequired(
|
||||
runtime = resolveOemCloudRuntimeContext(),
|
||||
): Promise<OemCloudStartupLoginResult> {
|
||||
if (!runtime) {
|
||||
return { status: "not_configured" };
|
||||
}
|
||||
|
||||
if (hasCurrentTenantSession(runtime)) {
|
||||
return { status: "has_session" };
|
||||
}
|
||||
|
||||
if (readStartupAttempt(runtime)) {
|
||||
return { status: "already_attempted" };
|
||||
}
|
||||
|
||||
const providers = await listPublicOAuthProviders(runtime.tenantId);
|
||||
if (!hasGoogleOAuthProvider(providers)) {
|
||||
return { status: "no_google_provider" };
|
||||
}
|
||||
|
||||
markStartupAttempt(runtime);
|
||||
try {
|
||||
await startOemCloudLogin(runtime);
|
||||
return { status: "started" };
|
||||
} catch (error) {
|
||||
const reason =
|
||||
error instanceof Error && error.message.trim()
|
||||
? error.message.trim()
|
||||
: "启动云端登录失败";
|
||||
console.warn("启动期云端登录失败:", error);
|
||||
return { status: "failed", reason };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user