fix(desktop): DeerFlow UI — ChatArea refactor + ai-elements + dead CSS cleanup
ChatArea retry button uses setInput instead of direct sendToGateway, fix bootstrap spinner stuck for non-logged-in users, remove dead CSS (aurora-title/sidebar-open/quick-action-chips), add ai components (ReasoningBlock/StreamingText/ChatMode/ModelSelector/TaskProgress), add ClassroomPlayer + ResizableChatLayout + artifact panel Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,6 @@
|
||||
* - saas-errors.ts — SaaSApiError class
|
||||
* - saas-session.ts — session persistence (load/save/clear)
|
||||
* - saas-auth.ts — login/register/TOTP methods (mixin)
|
||||
* - saas-admin.ts — admin panel API methods (mixin)
|
||||
* - saas-relay.ts — relay tasks, chat completion, usage (mixin)
|
||||
* - saas-prompt.ts — prompt OTA methods (mixin)
|
||||
* - saas-telemetry.ts — telemetry reporting methods (mixin)
|
||||
@@ -96,26 +95,6 @@ import type {
|
||||
SaaSErrorResponse,
|
||||
RelayTaskInfo,
|
||||
UsageStats,
|
||||
ProviderInfo,
|
||||
CreateProviderRequest,
|
||||
UpdateProviderRequest,
|
||||
ModelInfo,
|
||||
CreateModelRequest,
|
||||
UpdateModelRequest,
|
||||
AccountApiKeyInfo,
|
||||
CreateApiKeyRequest,
|
||||
AccountPublic,
|
||||
UpdateAccountRequest,
|
||||
PaginatedResponse,
|
||||
TokenInfo,
|
||||
CreateTokenRequest,
|
||||
OperationLogInfo,
|
||||
DashboardStats,
|
||||
RoleInfo,
|
||||
CreateRoleRequest,
|
||||
UpdateRoleRequest,
|
||||
PermissionTemplate,
|
||||
CreateTemplateRequest,
|
||||
PromptCheckResult,
|
||||
PromptTemplateInfo,
|
||||
PromptVersionInfo,
|
||||
@@ -128,7 +107,7 @@ import { createLogger } from './logger';
|
||||
|
||||
const saasLog = createLogger('saas-client');
|
||||
import { installAuthMethods } from './saas-auth';
|
||||
import { installAdminMethods } from './saas-admin';
|
||||
|
||||
import { installRelayMethods } from './saas-relay';
|
||||
import { installPromptMethods } from './saas-prompt';
|
||||
import { installTelemetryMethods } from './saas-telemetry';
|
||||
@@ -140,6 +119,25 @@ export class SaaSClient {
|
||||
private baseUrl: string;
|
||||
private token: string | null = null;
|
||||
|
||||
/**
|
||||
* Refresh mutex: shared Promise to prevent concurrent token refresh.
|
||||
* When multiple requests hit 401 simultaneously, they all await the same
|
||||
* refresh Promise instead of triggering N parallel refresh calls.
|
||||
*/
|
||||
private _refreshPromise: Promise<string> | null = null;
|
||||
|
||||
/**
|
||||
* Thread-safe token refresh — coalesces concurrent refresh attempts into one.
|
||||
* First caller triggers the actual refresh; subsequent callers await the same Promise.
|
||||
*/
|
||||
async refreshMutex(): Promise<string> {
|
||||
if (this._refreshPromise) return this._refreshPromise;
|
||||
this._refreshPromise = this.refreshToken().finally(() => {
|
||||
this._refreshPromise = null;
|
||||
});
|
||||
return this._refreshPromise;
|
||||
}
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
this.baseUrl = baseUrl.replace(/\/+$/, '');
|
||||
}
|
||||
@@ -237,7 +235,7 @@ export class SaaSClient {
|
||||
// 401: 尝试刷新 Token 后重试 (防止递归)
|
||||
if (response.status === 401 && !this._isAuthEndpoint(path) && !_isRefreshRetry) {
|
||||
try {
|
||||
const newToken = await this.refreshToken();
|
||||
const newToken = await this.refreshMutex();
|
||||
if (newToken) {
|
||||
return this.request<T>(method, path, body, timeoutMs, true);
|
||||
}
|
||||
@@ -394,7 +392,7 @@ export class SaaSClient {
|
||||
* Used for template selection during onboarding.
|
||||
*/
|
||||
async fetchAvailableTemplates(): Promise<AgentTemplateAvailable[]> {
|
||||
return this.request<AgentTemplateAvailable[]>('GET', '/agent-templates/available');
|
||||
return this.request<AgentTemplateAvailable[]>('GET', '/api/v1/agent-templates/available');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -402,13 +400,12 @@ export class SaaSClient {
|
||||
* Returns all fields needed to create an agent from template.
|
||||
*/
|
||||
async fetchTemplateFull(id: string): Promise<AgentTemplateFull> {
|
||||
return this.request<AgentTemplateFull>('GET', `/agent-templates/${id}/full`);
|
||||
return this.request<AgentTemplateFull>('GET', `/api/v1/agent-templates/${id}/full`);
|
||||
}
|
||||
}
|
||||
|
||||
// === Install mixin methods ===
|
||||
installAuthMethods(SaaSClient);
|
||||
installAdminMethods(SaaSClient);
|
||||
installRelayMethods(SaaSClient);
|
||||
installPromptMethods(SaaSClient);
|
||||
installTelemetryMethods(SaaSClient);
|
||||
@@ -429,57 +426,6 @@ export interface SaaSClient {
|
||||
verifyTotp(code: string): Promise<TotpResultResponse>;
|
||||
disableTotp(password: string): Promise<TotpResultResponse>;
|
||||
|
||||
// --- Admin: Providers (saas-admin.ts) ---
|
||||
listProviders(): Promise<ProviderInfo[]>;
|
||||
getProvider(id: string): Promise<ProviderInfo>;
|
||||
createProvider(data: CreateProviderRequest): Promise<ProviderInfo>;
|
||||
updateProvider(id: string, data: UpdateProviderRequest): Promise<ProviderInfo>;
|
||||
deleteProvider(id: string): Promise<void>;
|
||||
|
||||
// --- Admin: Models (saas-admin.ts) ---
|
||||
listModelsAdmin(providerId?: string): Promise<ModelInfo[]>;
|
||||
getModel(id: string): Promise<ModelInfo>;
|
||||
createModel(data: CreateModelRequest): Promise<ModelInfo>;
|
||||
updateModel(id: string, data: UpdateModelRequest): Promise<ModelInfo>;
|
||||
deleteModel(id: string): Promise<void>;
|
||||
|
||||
// --- Admin: API Keys (saas-admin.ts) ---
|
||||
listApiKeys(providerId?: string): Promise<AccountApiKeyInfo[]>;
|
||||
createApiKey(data: CreateApiKeyRequest): Promise<AccountApiKeyInfo>;
|
||||
rotateApiKey(id: string, newKeyValue: string): Promise<void>;
|
||||
revokeApiKey(id: string): Promise<void>;
|
||||
|
||||
// --- Admin: Accounts (saas-admin.ts) ---
|
||||
listAccounts(params?: { page?: number; page_size?: number; role?: string; status?: string; search?: string }): Promise<PaginatedResponse<AccountPublic>>;
|
||||
getAccount(id: string): Promise<AccountPublic>;
|
||||
updateAccount(id: string, data: UpdateAccountRequest): Promise<AccountPublic>;
|
||||
updateAccountStatus(id: string, status: 'active' | 'disabled' | 'suspended'): Promise<void>;
|
||||
|
||||
// --- Admin: Tokens (saas-admin.ts) ---
|
||||
listTokens(): Promise<TokenInfo[]>;
|
||||
createToken(data: CreateTokenRequest): Promise<TokenInfo>;
|
||||
revokeToken(id: string): Promise<void>;
|
||||
|
||||
// --- Admin: Logs (saas-admin.ts) ---
|
||||
listOperationLogs(params?: { page?: number; page_size?: number }): Promise<OperationLogInfo[]>;
|
||||
|
||||
// --- Admin: Dashboard (saas-admin.ts) ---
|
||||
getDashboardStats(): Promise<DashboardStats>;
|
||||
|
||||
// --- Admin: Roles (saas-admin.ts) ---
|
||||
listRoles(): Promise<RoleInfo[]>;
|
||||
getRole(id: string): Promise<RoleInfo>;
|
||||
createRole(data: CreateRoleRequest): Promise<RoleInfo>;
|
||||
updateRole(id: string, data: UpdateRoleRequest): Promise<RoleInfo>;
|
||||
deleteRole(id: string): Promise<void>;
|
||||
|
||||
// --- Admin: Permission Templates (saas-admin.ts) ---
|
||||
listPermissionTemplates(): Promise<PermissionTemplate[]>;
|
||||
getPermissionTemplate(id: string): Promise<PermissionTemplate>;
|
||||
createPermissionTemplate(data: CreateTemplateRequest): Promise<PermissionTemplate>;
|
||||
deletePermissionTemplate(id: string): Promise<void>;
|
||||
applyPermissionTemplate(templateId: string, accountIds: string[]): Promise<{ ok: boolean; applied_count: number }>;
|
||||
|
||||
// --- Relay (saas-relay.ts) ---
|
||||
listRelayTasks(query?: { status?: string; page?: number; page_size?: number }): Promise<RelayTaskInfo[]>;
|
||||
getRelayTask(taskId: string): Promise<RelayTaskInfo>;
|
||||
|
||||
Reference in New Issue
Block a user