5884 lines
244 KiB
TypeScript
5884 lines
244 KiB
TypeScript
import type { SqlFileEncoding } from "./tauri";
|
||
import type { StarRocksAlterOptions } from "@/lib/table/starrocksAlterOptions";
|
||
import type { CreateTableDialectOptions } from "@/lib/table/starrocksPhysicalOptions";
|
||
import type { MongoDumpFormat, MongoDumpSourceInput, MongoDumpCatalog, MongoRestoreSourcePreview, MongoDatabaseDumpRequest, MongoDatabaseRestoreRequest, MongoDatabaseDumpProgress } from "./mongodbDumpTypes";
|
||
import type { MongoRestoreUpload, MongoSourceReadOptions } from "./mongodbDumpTypes";
|
||
import type { DamengDumpKind, DamengDumpRequest, DamengDumpProgress } from "./damengDumpTypes";
|
||
import type { UserSkillRootSettings, UserSkillsListResult, UserSkillsReadResult } from "@/types/userSkills";
|
||
import type { DatabaseBackupCommand, DatabaseBackupBackgroundStatus } from "@/lib/backup/backgroundDatabaseBackup";
|
||
import type { PluginUiStorageItemRef, SqlFileFailureLogExportPayload, SqlFileFailureLogInfo, SqlFileFailureLogKey, SqlFileFailureLogTextPayload, SyncCatalogItem, SyncSelection, SyncSnapshotCatalog } from "@/lib/backend/tauri";
|
||
import { FAILURE_LOG_TEXT_TOO_LARGE } from "@/lib/export/failureLogExport";
|
||
import { saveTextFile } from "@/lib/export/saveTextFile";
|
||
import { functionDiffsToWire, functionInfoFromWire, schemaDiffPreparationFromWire, schemaDiffPreparationToWire, type FunctionInfoWire, type SchemaDiffPreparationWire } from "./functionInfoTransport";
|
||
import type { OracleRoutineSnapshot, OracleRoutineOperation, OracleRoutineOperationObservation, OracleRoutineSaveInput, OracleRoutinePlanInput, OracleRoutinePlan } from "@/types/schemaDiff";
|
||
|
||
export function databaseBackupCommand<T = unknown>(command: DatabaseBackupCommand): Promise<T> {
|
||
return post("/api/database-backups", command);
|
||
}
|
||
|
||
export async function databaseBackupBackground(enabled?: boolean): Promise<DatabaseBackupBackgroundStatus> {
|
||
if (enabled !== undefined) throw new Error("The server manages background backups");
|
||
return { enabled: true, platform: "server" };
|
||
}
|
||
|
||
export async function downloadDatabaseBackupFile(runId: string, index: number): Promise<void> {
|
||
const anchor = document.createElement("a");
|
||
anchor.href = apiUrl(`/api/database-backups/${encodeURIComponent(runId)}/files/${index}`);
|
||
anchor.click();
|
||
}
|
||
|
||
export function prepareDatabaseBackupRestore(runId: string, index: number): Promise<SqlFilePreview> {
|
||
return post(`/api/database-backups/${encodeURIComponent(runId)}/files/${index}/restore`, {});
|
||
}
|
||
import type {
|
||
ConnectionConfig,
|
||
ConnectionTestResult,
|
||
DatabaseConnectionInfo,
|
||
DatabaseInfo,
|
||
DatabaseStorageInfo,
|
||
XuguTablespaceInfo,
|
||
SqlServerCompletionContext,
|
||
SchemaInfo,
|
||
LinkedServerInfo,
|
||
CatalogInfo,
|
||
TableInfo,
|
||
TableNameFilter,
|
||
ObjectInfo,
|
||
CompletionAssistantRequest,
|
||
CompletionAssistantResponse,
|
||
ObjectStatistics,
|
||
CustomTypeDetails,
|
||
OracleTypeManagementSnapshot,
|
||
OracleTypeManagementObjectKey,
|
||
OracleTypeManagementSqlInput,
|
||
OracleTypeManagementSqlResult,
|
||
OracleTypeDataRequest,
|
||
OracleTypeDataResult,
|
||
OracleTypeEvolutionRequest,
|
||
OracleTypeEvolutionManifest,
|
||
ObjectSource,
|
||
ObjectSourceKind,
|
||
MysqlEventInfo,
|
||
ColumnInfo,
|
||
SqlServerColumnMetadata,
|
||
IndexInfo,
|
||
ReferenceKeyInfo,
|
||
ForeignKeyInfo,
|
||
TriggerInfo,
|
||
ConstraintInfo,
|
||
PartitionInfo,
|
||
SubpartitionInfo,
|
||
ExtensionInfo,
|
||
EventTriggerInfo,
|
||
FunctionInfo,
|
||
SequenceInfo,
|
||
RuleInfo,
|
||
OwnerInfo,
|
||
QueryResult,
|
||
SqlReferenceAnalysis,
|
||
DatabaseType,
|
||
InstalledPlugin,
|
||
JdbcDriverInfo,
|
||
JdbcLocalBundleInfo,
|
||
JdbcMavenBundleInfo,
|
||
JdbcPluginStatus,
|
||
SidebarLayout,
|
||
TableVGroupLayout,
|
||
SavedSqlFile,
|
||
SavedSqlFolder,
|
||
SavedSqlLibrary,
|
||
SshConfigHostEntry,
|
||
LocalSshKey,
|
||
SshAgentIdentity,
|
||
TunnelProfile,
|
||
} from "@/types/database";
|
||
import type { DetachedTabHandoff } from "@/lib/app/detachedTabHandoff";
|
||
import { normalizeRustMongoCommand, type MongoCommand } from "@/lib/mongo/mongoShellCommand";
|
||
import type { MongoBulkWriteResult } from "@/lib/mongo/mongoShellCommand";
|
||
import { BackendErrorException, type BackendError } from "@/lib/backend/errorUtils";
|
||
import { decodeMeilisearchDocumentPage, decodeMeilisearchSearchResult, type MeilisearchDocumentPage, type MeilisearchDocumentPageWire, type MeilisearchSearchResult, type MeilisearchSearchWireResult } from "@/lib/backend/meilisearchTransport";
|
||
import type { CreatedKey, EnqueuedTaskSummary, KeyCreateInput, KeyListItem, KeyPage, KeyUpdateInput, MeilisearchCreateIndexInput, MeilisearchSystemOverview, MeilisearchTask, TaskListInput, TaskPage, TaskSelector } from "@/types/meilisearchManagement";
|
||
import type { CollectionInfo } from "@/types/database";
|
||
import type { SchemaDiffPreparation, SchemaDiffPreparationOptions, SchemaSyncSqlPlan, SelectedSchemaDiffInput, GenerateSchemaSyncPlanOptions, TableDiff, FunctionDiff, SequenceDiff, RuleDiff, OwnerDiff } from "@/lib/schema/schemaDiff";
|
||
import type { SidebarObjectKind } from "@/lib/database/databaseObjectCapabilities";
|
||
import type { AiConfig, AiTestConnectionResult } from "@/stores/settingsStore";
|
||
import type { AiChatSelectionState, AiEffortCapability } from "@/types/ai";
|
||
import type { SalesforceCurrentUser, SalesforceOAuthAuthorizeParams, SalesforceOAuthDevicePollResult, SalesforceOAuthDeviceStartResult, SalesforceOAuthRefreshResult, SalesforceOAuthToken } from "@/types/salesforce";
|
||
import type {
|
||
AgentDriverInfo,
|
||
AiCompletionRequest,
|
||
AiStreamChunk,
|
||
AiConversation,
|
||
AiRun,
|
||
AiModelInfo,
|
||
DriverStoreUsage,
|
||
DriverRuntimeSummary,
|
||
UpgradeAllAgentDriversResult,
|
||
AgentOfflineImportResult,
|
||
AgentUpdateBlocker,
|
||
AgentOfflineExportPreview,
|
||
AgentOfflineExportResult,
|
||
DesktopSettings,
|
||
McpGlobalPolicy,
|
||
SavedSqlSyncRequest,
|
||
DriverInstallProgress,
|
||
JavaRuntimeConfig,
|
||
UpdateInfo,
|
||
DownloadedUpdate,
|
||
UpdateDownloadSource,
|
||
RedisCollectionPage,
|
||
RedisBlob,
|
||
RedisDatabaseInfo,
|
||
RedisStreamConsumer,
|
||
RedisStreamGroup,
|
||
RedisStreamPage,
|
||
RedisStreamPendingPage,
|
||
RedisValue,
|
||
RedisKeysExpiryResult,
|
||
RedisScanResult,
|
||
RedisCommandResult,
|
||
RedisSlowlogEntry,
|
||
RedisNodeEndpoint,
|
||
KvInt64,
|
||
KvValue,
|
||
KvListPrefixResponse,
|
||
KvListPrefixOptions,
|
||
KvGetResponse,
|
||
KvGetOptions,
|
||
KvPutOptions,
|
||
KvPutResponse,
|
||
KvDeleteOptions,
|
||
KvDeleteResponse,
|
||
KvHistoryResponse,
|
||
KvStatusResponse,
|
||
EtcdDefragResponse,
|
||
EtcdWatchStartRequest,
|
||
EtcdWatchStartResponse,
|
||
EtcdWatchPollResponse,
|
||
EtcdLeaseListResponse,
|
||
DocumentQueryResult,
|
||
DynamoDbTableDescription,
|
||
MongoDocumentResult,
|
||
MongoCollectionStatsResult,
|
||
MongoCloneCollectionResult,
|
||
MongoDropIndexesResult,
|
||
MongoIndexSpec,
|
||
MongoGridFsBucketInfo,
|
||
HistoryEntry,
|
||
HistorySearchRequest,
|
||
HistorySearchResult,
|
||
HistoryConnectionOption,
|
||
TaskRunDetail,
|
||
TaskRunItemsPage,
|
||
TaskRunItemsQuery,
|
||
TaskRunListQuery,
|
||
TaskRunPage,
|
||
SqlFileRequest,
|
||
SqlFilePreview,
|
||
SqlFileTable,
|
||
SqlFileProgress,
|
||
TransferRequest,
|
||
TransferProgress,
|
||
TransferOwnershipPreview,
|
||
TableImportPreviewRequest,
|
||
TableImportPreview,
|
||
TableImportRequest,
|
||
TableImportSummary,
|
||
TableImportProgress,
|
||
MongoImportPreviewRequest,
|
||
MongoImportPreview,
|
||
MongoImportRequest,
|
||
MongoImportProgress,
|
||
MongoImportSummary,
|
||
MongoExportRequest,
|
||
MongoExportProgress,
|
||
MongoExportSummary,
|
||
DatabaseBackupSnapshot,
|
||
DatabaseExportRequest,
|
||
ExportProgress,
|
||
TableExportRequest,
|
||
TableExportProgress,
|
||
QueryResultExportRequest,
|
||
TableCsvExportOptions,
|
||
XlsxCellValue,
|
||
QueryPaginationExecutionPlanOptions,
|
||
QueryPaginationExecutionPlan,
|
||
SortedQuerySqlOptions,
|
||
QuerySqlBuildResult,
|
||
BuildExplainSqlOptions,
|
||
ExplainSqlBuildResult,
|
||
PluginPlanCapabilities,
|
||
PluginPlanRequest,
|
||
PluginPlanResult,
|
||
DroppedFilePreviewSqlOptions,
|
||
MongoGridFsFileInfo,
|
||
AppSupportInfo,
|
||
PromptTemplate,
|
||
SshPromptResolution,
|
||
MeilisearchIndexOverview,
|
||
MeilisearchIndexSettings,
|
||
ElasticsearchIndexMetadataKind,
|
||
ElasticsearchDeleteByQueryResult,
|
||
} from "@/lib/backend/tauri";
|
||
import type { QueryEditability } from "@/lib/sql/sqlAnalysis";
|
||
import type { PluginTableMetadata, PluginTableMetadataRequest } from "@/types/pluginSchemaMetadata";
|
||
import type { PluginDataGrant, PluginDataQueryRequest, PluginDataQueryResult } from "@/types/pluginData";
|
||
import type { PluginToolPreview } from "@/types/pluginAiTools";
|
||
import type { CsvQuoteMode } from "@/lib/export/csvQuoteMode";
|
||
import type { MigrationPreflight, MigrationReport } from "./migration";
|
||
export type { MigrationPreflight, MigrationReport } from "./migration";
|
||
export const migrationStatus = (retry = false): Promise<MigrationPreflight> => get(`/api/migration/status${retry ? "?retry=true" : ""}`);
|
||
export const migrationStart = (): Promise<MigrationReport> => post("/api/migration/start", {});
|
||
export const migrationRetry = (): Promise<MigrationReport> => post("/api/migration/retry", {});
|
||
export const migrationCleanupBackups = (): Promise<void> => post("/api/migration/cleanup-backups", {});
|
||
import { isTerminalTransferProgress } from "@/lib/backend/transferProgress";
|
||
import type {
|
||
DataGridColumnDistinctValuesSqlOptions,
|
||
DataGridColumnValueFilterConditionOptions,
|
||
DataGridColumnValuesFilterConditionOptions,
|
||
DataGridContextFilterConditionOptions,
|
||
DataGridConditionalUpdateSqlOptions,
|
||
DataGridCountSqlOptions,
|
||
DataGridCopyInsertStatementOptions,
|
||
DataGridCopyUpdateStatementOptions,
|
||
DataGridSaveStatementOptions,
|
||
HiveTablePropertiesSqlOptions,
|
||
} from "@/lib/dataGrid/dataGridSql";
|
||
import type { DmlChangePreviewSqlOptions, DmlChangePreviewSqlResult } from "@/lib/sql/dmlChangePreview";
|
||
import type { DataGridExtractRequest, DataGridExtractResult } from "@/lib/dataGrid/dataGridCopyExtractor";
|
||
import type {
|
||
BuildCreatePartitionedTableSqlOptions,
|
||
BuildTableOwnerChangeSqlOptions,
|
||
BuildTableStructureChangeSqlOptions,
|
||
BuildSingleColumnAlterSqlOptions,
|
||
MysqlTableCharsetChange,
|
||
SqliteTableStructureChangePreview,
|
||
TablePartitionSqlOptions,
|
||
TableStructureChangeSql,
|
||
} from "@/lib/table/tableStructureEditorSql";
|
||
import type { BuildTableSelectSqlOptions } from "@/lib/table/tableSelectSql";
|
||
import type { DatabaseSearchSql, DatabaseSearchSqlOptions, SearchResultWhereOptions } from "@/lib/database/databaseSearch";
|
||
import type { DatabaseStructureSearchSql, DatabaseStructureSearchSqlOptions } from "@/lib/database/databaseStructureSearch";
|
||
import type { BuildEditableObjectSourceSqlInput, BuildRoutineRenameObjectSourceInput, FingerprintOracleRoutineObjectSourceInput, RewriteOracleRoutineReferencesInput, ValidateOracleExecutableObjectSourceInput } from "@/lib/table/objectSourceEditor";
|
||
import type { OracleTableReferenceRewriteInput, OracleViewRenameSource, OracleViewRenameSourceInput, OracleViewRenameSourceReview } from "@/types/database";
|
||
import type { BuildViewDdlInput } from "@/lib/table/viewDdl";
|
||
import type { BuildRenameObjectSqlOptions } from "@/lib/table/objectRenameSql";
|
||
import type { CreateDatabaseSqlOptions } from "@/lib/database/createDatabaseSql";
|
||
import type {
|
||
DatabaseNameSqlOptions,
|
||
DatabasePropertyEditSqlOptions,
|
||
DropTableChildObjectSqlOptions,
|
||
DropObjectSqlOptions,
|
||
DuplicateTableStructureSqlOptions,
|
||
CopyTableDataSqlOptions,
|
||
MysqlAutoIncrementSqlOptions,
|
||
SchemaNameSqlOptions,
|
||
TableAdminSqlOptions,
|
||
VacuumTableSqlOptions,
|
||
} from "@/lib/database/dbAdminSql";
|
||
import type { BuildDatabaseSqlExportOptions, BuildExportInsertStatementsOptions, BuildExportSqlInsertOptions } from "@/lib/export/databaseExport";
|
||
import { loadBrowserAppState, saveBrowserAppState } from "@/lib/backend/browserAppStateStorage";
|
||
import type { DataCompareFromTablesOptions, DataCompareFromTablesPreparation, DataCompareSyncPlan, DataCompareSyncPlanOptions, DataComparePreparation, DataComparePreparationOptions } from "@/lib/dataGrid/dataCompare";
|
||
import { apiUrl, apiWebSocketUrl } from "@/lib/common/webPath";
|
||
import type {
|
||
ActivePluginSession,
|
||
ConnectionIdleClosedEvent,
|
||
ConnectionLivenessMessage,
|
||
PluginBinaryEvent,
|
||
PluginConnectionActionResult,
|
||
PluginEvent,
|
||
PluginFilesystemListResult,
|
||
PluginFilesystemMutationResult,
|
||
PluginFilesystemReadResult,
|
||
PluginInstallResult,
|
||
PluginMarketplaceInstallRequest,
|
||
PluginRepository,
|
||
PluginRepositoryCatalogResult,
|
||
PluginRollbackResult,
|
||
PluginTrustedKey,
|
||
PluginUiAssetPayload,
|
||
} from "@/types/database";
|
||
import type { BlobBoundStatement, DataGridSavePreparation } from "@/lib/backend/tauri";
|
||
import type {
|
||
NacosBatchPreview,
|
||
NacosBatchReport,
|
||
NacosConfigSelector,
|
||
NacosConfigTransferRequest,
|
||
NacosConflictPolicy,
|
||
NacosContentSearchRequest,
|
||
NacosContentSearchResult,
|
||
NacosConfigHistoryKey,
|
||
NacosConfigHistoryList,
|
||
NacosConfigHistoryQuery,
|
||
NacosConfigItem,
|
||
NacosConfigKey,
|
||
NacosConfigList,
|
||
NacosConfigQuery,
|
||
NacosConfigRollbackRequest,
|
||
NacosConfigUpsert,
|
||
NacosConnectionInfo,
|
||
NacosRNacosConsoleCaptcha,
|
||
NacosUserQuery,
|
||
NacosUserList,
|
||
NacosUserCreate,
|
||
NacosUserUpdate,
|
||
NacosRoleQuery,
|
||
NacosRoleList,
|
||
NacosRoleBinding,
|
||
NacosAccessControlSnapshot,
|
||
NacosAccessOperationRequest,
|
||
NacosAccessOperationResult,
|
||
NacosAccessOperationRetry,
|
||
NacosInstanceInfo,
|
||
NacosInstanceRef,
|
||
NacosInstanceRegistration,
|
||
NacosInstanceQuery,
|
||
NacosInstanceUpdateRequest,
|
||
NacosDashboardQuery,
|
||
NacosDashboardSnapshot,
|
||
NacosNamespaceCreate,
|
||
NacosNamespaceInfo,
|
||
NacosNamespaceSidebarSnapshot,
|
||
NacosNamespaceUpdate,
|
||
NacosRawRequest,
|
||
NacosRawResponse,
|
||
NacosServiceList,
|
||
NacosServiceDetail,
|
||
NacosServiceQuery,
|
||
NacosServiceUpsert,
|
||
NacosSearchProgress,
|
||
} from "@/types/nacos";
|
||
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage";
|
||
import { appendDebugLog, isDebugLoggingEnabled } from "@/lib/backend/debugLog";
|
||
import { collectBrowserSupportInfo } from "@/lib/app/supportInfo";
|
||
import { normalizeConnectionTestResult } from "@/lib/connection/connectionDatabaseInfo";
|
||
import type { AnnotationFile, SchemaSnapshot } from "@/docs/types";
|
||
import { uuid } from "@/lib/common/utils";
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Helpers
|
||
// ---------------------------------------------------------------------------
|
||
|
||
const DESKTOP_SETTINGS_STORAGE_KEY = "dbx-desktop-settings";
|
||
const DEFAULT_DESKTOP_SETTINGS: DesktopSettings = {
|
||
show_tray_icon: true,
|
||
linux_floating_frame_enabled: true,
|
||
icon_theme: "default",
|
||
quit_on_close: false,
|
||
close_action_prompted: false,
|
||
debug_logging_enabled: false,
|
||
metadata_cache_max_memory_mb: 64,
|
||
duckdb_worker_process_isolation: false,
|
||
duckdb_worker_max_processes: 4,
|
||
saved_sql_sync_dir: null,
|
||
driver_store_dir: null,
|
||
plugin_store_dir: null,
|
||
agent_store_dir: null,
|
||
custom_ai_skill_root_enabled: false,
|
||
custom_ai_skill_root: null,
|
||
custom_ai_skill_auto_enabled: false,
|
||
sidebar_table_page_size: 1000,
|
||
};
|
||
|
||
async function post<T>(url: string, body: unknown): Promise<T> {
|
||
const res = await fetch(apiUrl(url), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify(body),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
if (res.status === 204) return undefined as T;
|
||
return res.json();
|
||
}
|
||
|
||
async function postQueryWithDiagnostics<T>(url: string, body: unknown, traceId?: string): Promise<T> {
|
||
if (!isDebugLoggingEnabled()) return post(url, body);
|
||
|
||
const startedAt = performance.now();
|
||
const serializedBody = JSON.stringify(body);
|
||
const response = await fetch(apiUrl(url), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: serializedBody,
|
||
});
|
||
const headersAt = performance.now();
|
||
const responseText = await response.text();
|
||
const bodyAt = performance.now();
|
||
if (!response.ok) {
|
||
appendDebugLog("warn", "[DBX][query-transport:http:error]", {
|
||
traceId: traceId?.slice(0, 8),
|
||
status: response.status,
|
||
requestBytes: new TextEncoder().encode(serializedBody).byteLength,
|
||
responseBytes: new TextEncoder().encode(responseText).byteLength,
|
||
responseHeadersMs: Math.round(headersAt - startedAt),
|
||
responseBodyMs: Math.round(bodyAt - headersAt),
|
||
totalMs: Math.round(bodyAt - startedAt),
|
||
backendCoreMs: response.headers.get("x-dbx-core-ms"),
|
||
backendSerializeMs: response.headers.get("x-dbx-serialize-ms"),
|
||
});
|
||
throw await backendResponseError(
|
||
new Response(responseText, {
|
||
status: response.status,
|
||
statusText: response.statusText,
|
||
headers: response.headers,
|
||
}),
|
||
);
|
||
}
|
||
const result = JSON.parse(responseText) as T;
|
||
const parsedAt = performance.now();
|
||
appendDebugLog("info", "[DBX][query-transport:http]", {
|
||
traceId: traceId?.slice(0, 8),
|
||
requestBytes: new TextEncoder().encode(serializedBody).byteLength,
|
||
responseBytes: new TextEncoder().encode(responseText).byteLength,
|
||
responseHeadersMs: Math.round(headersAt - startedAt),
|
||
responseBodyMs: Math.round(bodyAt - headersAt),
|
||
jsonParseMs: Math.round(parsedAt - bodyAt),
|
||
totalMs: Math.round(parsedAt - startedAt),
|
||
backendCoreMs: response.headers.get("x-dbx-core-ms"),
|
||
backendSerializeMs: response.headers.get("x-dbx-serialize-ms"),
|
||
});
|
||
return result;
|
||
}
|
||
|
||
async function get<T>(url: string): Promise<T> {
|
||
const res = await fetch(apiUrl(url));
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
async function del<T>(url: string): Promise<T> {
|
||
const res = await fetch(apiUrl(url), { method: "DELETE" });
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
async function put<T>(url: string, body: unknown): Promise<T> {
|
||
const res = await fetch(apiUrl(url), {
|
||
method: "PUT",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify(body),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
export async function backendResponseError(response: Response): Promise<BackendErrorException> {
|
||
const text = await response.text();
|
||
let payload: unknown = text;
|
||
try {
|
||
payload = JSON.parse(text);
|
||
} catch {
|
||
// Preserve legacy plain-text responses at the same compatibility boundary.
|
||
}
|
||
return new BackendErrorException(payload);
|
||
}
|
||
|
||
function qs(params: Record<string, string | number | boolean | undefined>): string {
|
||
const sp = new URLSearchParams();
|
||
for (const [k, v] of Object.entries(params)) {
|
||
if (v !== undefined && v !== null) sp.set(k, String(v));
|
||
}
|
||
return sp.toString();
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Connection
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function testConnection(config: ConnectionConfig): Promise<string> {
|
||
return post("/api/connection/test", { config });
|
||
}
|
||
|
||
export async function testSshTunnel(config: ConnectionConfig): Promise<string> {
|
||
return post("/api/connection/test-ssh-tunnel", { config });
|
||
}
|
||
|
||
export async function testConnectionWithInfo(config: ConnectionConfig): Promise<ConnectionTestResult> {
|
||
const response = await fetch(apiUrl("/api/connection/test-info"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ config }),
|
||
});
|
||
if (response.status === 404) {
|
||
return normalizeConnectionTestResult(await testConnection(config), config);
|
||
}
|
||
if (!response.ok) throw await backendResponseError(response);
|
||
return normalizeConnectionTestResult(await response.json(), config);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Salesforce OAuth (browser redirect + device-code flows)
|
||
// ---------------------------------------------------------------------------
|
||
// Web mode only supports the device-code flow end-to-end; the browser-redirect
|
||
// flow is gated by the desktop shell (it binds a localhost callback server).
|
||
// The HTTP endpoints mirror the Tauri commands one-for-one so `api.ts` can
|
||
// forward both transports through the same surface.
|
||
|
||
export async function salesforceOauthBrowserAuthorize(params: SalesforceOAuthAuthorizeParams): Promise<SalesforceOAuthToken> {
|
||
return post("/api/connection/salesforce-oauth-browser-authorize", { params });
|
||
}
|
||
|
||
export async function salesforceOauthDeviceStart(params: SalesforceOAuthAuthorizeParams): Promise<SalesforceOAuthDeviceStartResult> {
|
||
return post("/api/connection/salesforce-oauth-device-start", { params });
|
||
}
|
||
|
||
export async function salesforceOauthDevicePoll(params: SalesforceOAuthAuthorizeParams, deviceCode: string, intervalSecs: number): Promise<SalesforceOAuthDevicePollResult> {
|
||
return post("/api/connection/salesforce-oauth-device-poll", { params, deviceCode, intervalSecs });
|
||
}
|
||
|
||
export async function salesforceOauthRefresh(params: SalesforceOAuthAuthorizeParams, refreshToken: string): Promise<SalesforceOAuthRefreshResult> {
|
||
return post("/api/connection/salesforce-oauth-refresh", { params, refreshToken });
|
||
}
|
||
|
||
export async function salesforceOauthPasswordLogin(params: SalesforceOAuthAuthorizeParams, username: string, password: string): Promise<SalesforceOAuthToken> {
|
||
return post("/api/connection/salesforce-oauth-password-login", { params, username, password });
|
||
}
|
||
|
||
/** Identity of the user an established Salesforce connection is authenticated as (cached backend-side). */
|
||
export async function salesforceCurrentUser(connectionId: string): Promise<SalesforceCurrentUser> {
|
||
return get(`/api/salesforce/current-user?${qs({ connection_id: connectionId })}`);
|
||
}
|
||
|
||
export async function connectDb(config: ConnectionConfig, clientAttempt?: number): Promise<string> {
|
||
return post("/api/connection/connect", { config, clientAttempt });
|
||
}
|
||
|
||
export async function connectionDatabaseInfo(connectionId: string, database?: string): Promise<DatabaseConnectionInfo | undefined> {
|
||
const info = await post<DatabaseConnectionInfo | null>("/api/connection/database-info", { connectionId, database });
|
||
return info ?? undefined;
|
||
}
|
||
|
||
export async function saveConnectionDatabaseInfo(connectionId: string, databaseInfo: DatabaseConnectionInfo): Promise<void> {
|
||
return post("/api/connection/database-info/save", {
|
||
connectionId,
|
||
databaseInfo,
|
||
});
|
||
}
|
||
|
||
export async function unlockConnectionWrites(connectionId: string, durationSecs: number): Promise<number> {
|
||
const state = await post<{ remainingMs: number }>("/api/connection/write-unlock", { connectionId, durationSecs });
|
||
return state.remainingMs;
|
||
}
|
||
|
||
export async function lockConnectionWrites(connectionId: string): Promise<void> {
|
||
return post("/api/connection/write-unlock/lock", { connectionId });
|
||
}
|
||
|
||
export async function connectionWriteUnlockState(connectionId: string): Promise<number> {
|
||
const state = await post<{ remainingMs: number }>("/api/connection/write-unlock/state", { connectionId });
|
||
return state.remainingMs;
|
||
}
|
||
|
||
export async function connectionFinalProxyPort(config: ConnectionConfig): Promise<number> {
|
||
return post("/api/connection/final-proxy-port", { config });
|
||
}
|
||
|
||
export async function disconnectDb(connectionId: string, clientAttempt?: number): Promise<void> {
|
||
return post("/api/connection/disconnect", { connectionId, clientAttempt });
|
||
}
|
||
|
||
export async function sessionCredentialStatus(connectionId: string): Promise<boolean> {
|
||
return post("/api/connection/session-credential-status", { connectionId });
|
||
}
|
||
|
||
export async function forgetSessionCredential(connectionId: string): Promise<void> {
|
||
return post("/api/connection/forget-session-credential", { connectionId });
|
||
}
|
||
|
||
export async function replaceNacosSessionCredential(connectionId: string, username: string, password: string): Promise<void> {
|
||
return post("/api/connection/replace-nacos-session-credential", { connectionId, username, password });
|
||
}
|
||
|
||
export async function checkConnectionHealth(connectionId: string): Promise<void> {
|
||
return post("/api/connection/check-health", { connectionId });
|
||
}
|
||
|
||
/**
|
||
* Read-only counterpart of `checkConnectionHealth`: reports whether the connection still has a
|
||
* pool, without probing or mutating anything (#4339).
|
||
*
|
||
* Liveness events must be confirmed through this, never through `checkConnectionHealth`: the
|
||
* latter removes unhealthy pools and is the path `ensureConnected` uses to trigger a reconnect.
|
||
*/
|
||
export async function connectionIsOpen(connectionId: string): Promise<boolean> {
|
||
return post("/api/connection/is-open", { connectionId });
|
||
}
|
||
|
||
export async function prewarmConnection(connectionId: string, database?: string, catalog?: string, clientSessionId?: string): Promise<void> {
|
||
return post("/api/connection/prewarm", { connectionId, database, catalog, clientSessionId });
|
||
}
|
||
|
||
export async function connectionIdentifierQuote(connectionId: string, database?: string): Promise<string | undefined> {
|
||
const quote = await post<string | null>("/api/connection/identifier-quote", {
|
||
connectionId,
|
||
database,
|
||
});
|
||
return quote ?? undefined;
|
||
}
|
||
|
||
export async function closeDatabaseConnection(connectionId: string, database: string): Promise<boolean> {
|
||
return post("/api/connection/close-database", { connectionId, database });
|
||
}
|
||
|
||
export async function saveConnections(configs: ConnectionConfig[], removedIds: string[] = []): Promise<void> {
|
||
// Saving upserts; ids this client deleted are sent explicitly so that a
|
||
// stale local list can never drop connections another client created.
|
||
if (removedIds.length) {
|
||
return post("/api/connection/save", { configs, removedIds });
|
||
}
|
||
return post("/api/connection/save", { configs });
|
||
}
|
||
|
||
export async function loadConnections(): Promise<ConnectionConfig[]> {
|
||
return get("/api/connection/list");
|
||
}
|
||
|
||
export async function loadTunnelProfiles(): Promise<TunnelProfile[]> {
|
||
return get("/api/tunnel-profiles/list");
|
||
}
|
||
|
||
export async function saveTunnelProfiles(profiles: TunnelProfile[]): Promise<void> {
|
||
return post("/api/tunnel-profiles/save", { profiles });
|
||
}
|
||
|
||
export async function testTunnelProfile(profile: TunnelProfile): Promise<string> {
|
||
return post("/api/tunnel-profiles/test", profile);
|
||
}
|
||
|
||
export async function listSshAgentIdentities(_sshAgentSockPath: string): Promise<SshAgentIdentity[]> {
|
||
return []; // The web backend cannot reach a local SSH agent socket
|
||
}
|
||
|
||
export async function resolveSshPrompt(resolution: SshPromptResolution): Promise<void> {
|
||
await post("/api/ssh/prompts/resolve", resolution);
|
||
}
|
||
|
||
export async function readKeychainPassword(_service: string): Promise<string> {
|
||
return ""; // Not available in web backend
|
||
}
|
||
|
||
export async function readKeychainPasswords(services: string[]): Promise<[string, string][]> {
|
||
return services.map((s) => [s, ""]); // Not available in web backend
|
||
}
|
||
|
||
export async function decryptConfig(payload: unknown, passphrase: string): Promise<string> {
|
||
return post("/api/app-settings/config/decrypt", { payload, passphrase });
|
||
}
|
||
|
||
export async function listSystemFonts(): Promise<string[]> {
|
||
return get("/api/system/fonts");
|
||
}
|
||
|
||
export async function listSshConfigHosts(): Promise<SshConfigHostEntry[]> {
|
||
return get("/api/ssh/config-hosts");
|
||
}
|
||
|
||
export async function listLocalSshKeys(): Promise<LocalSshKey[]> {
|
||
console.warn("listLocalSshKeys: local SSH key discovery is not available in the web backend");
|
||
return [];
|
||
}
|
||
|
||
export async function listPlugins(): Promise<InstalledPlugin[]> {
|
||
return get("/api/plugins");
|
||
}
|
||
|
||
export async function listPluginTrustedKeys(): Promise<PluginTrustedKey[]> {
|
||
return get("/api/plugins/trusted-keys");
|
||
}
|
||
|
||
export async function savePluginTrustedKey(keyId: string, publicKey: string): Promise<PluginTrustedKey[]> {
|
||
return post("/api/plugins/trusted-keys/save", { keyId, publicKey });
|
||
}
|
||
|
||
export async function removePluginTrustedKey(keyId: string): Promise<PluginTrustedKey[]> {
|
||
return post("/api/plugins/trusted-keys/remove", { keyId });
|
||
}
|
||
|
||
export async function listPluginRepositories(): Promise<PluginRepository[]> {
|
||
return get("/api/plugins/repositories");
|
||
}
|
||
|
||
export async function savePluginRepository(repository: PluginRepository): Promise<PluginRepository[]> {
|
||
return post("/api/plugins/repositories/save", repository);
|
||
}
|
||
|
||
export async function removePluginRepository(repositoryId: string): Promise<PluginRepository[]> {
|
||
return post("/api/plugins/repositories/remove", { repositoryId });
|
||
}
|
||
|
||
export async function fetchPluginMarketplaceCatalogs(): Promise<PluginRepositoryCatalogResult[]> {
|
||
return get("/api/plugins/marketplace/catalogs");
|
||
}
|
||
|
||
export async function installMarketplacePlugin(request: PluginMarketplaceInstallRequest): Promise<PluginInstallResult> {
|
||
return post("/api/plugins/marketplace/install", request);
|
||
}
|
||
|
||
export async function installPluginPackage(pathOrFile: string | File, allowUnsigned = false): Promise<PluginInstallResult> {
|
||
let blob: Blob;
|
||
let fileName: string;
|
||
if (pathOrFile instanceof File) {
|
||
blob = pathOrFile;
|
||
fileName = pathOrFile.name;
|
||
} else {
|
||
fileName = pathOrFile.split("/").pop() || "plugin.dbxp";
|
||
blob = await (await fetch(pathOrFile)).blob();
|
||
}
|
||
const formData = new FormData();
|
||
formData.append("file", blob, fileName);
|
||
const response = await fetch(apiUrl(`/api/plugins/install?allow_unsigned=${allowUnsigned}`), { method: "POST", body: formData });
|
||
if (!response.ok) throw new Error(await response.text());
|
||
return response.json();
|
||
}
|
||
|
||
export async function installPluginPackageFromUrl(url: string, allowUnsigned = false): Promise<PluginInstallResult> {
|
||
let blob: Blob;
|
||
let fileName: string;
|
||
try {
|
||
fileName = new URL(url).pathname.split("/").pop() || "plugin.dbxp";
|
||
} catch {
|
||
fileName = "plugin.dbxp";
|
||
}
|
||
blob = await (await fetch(url)).blob();
|
||
const formData = new FormData();
|
||
formData.append("file", blob, fileName);
|
||
const response = await fetch(apiUrl(`/api/plugins/install?allow_unsigned=${allowUnsigned}&from_url=true`), { method: "POST", body: formData });
|
||
if (!response.ok) throw new Error(await response.text());
|
||
return response.json();
|
||
}
|
||
|
||
export async function rollbackPlugin(pluginId: string): Promise<PluginRollbackResult> {
|
||
return post("/api/plugins/rollback", { plugin_id: pluginId });
|
||
}
|
||
|
||
export async function uninstallPlugin(pluginId: string): Promise<InstalledPlugin[]> {
|
||
return post("/api/plugins/uninstall", { plugin_id: pluginId });
|
||
}
|
||
|
||
export async function activatePlugin(pluginId: string): Promise<ActivePluginSession[]> {
|
||
return post("/api/plugins/activate", { plugin_id: pluginId });
|
||
}
|
||
|
||
export async function listActivePlugins(): Promise<ActivePluginSession[]> {
|
||
return get("/api/plugins/active");
|
||
}
|
||
|
||
export async function stopPlugin(pluginId: string): Promise<void> {
|
||
await post("/api/plugins/stop", { plugin_id: pluginId });
|
||
}
|
||
|
||
export async function invokePlugin<T = unknown>(pluginId: string, method: string, params: unknown = null, timeoutMs?: number): Promise<T> {
|
||
return post("/api/plugins/invoke", { pluginId, method, params, timeoutMs });
|
||
}
|
||
|
||
export async function invokePluginConnectionAction(config: ConnectionConfig, actionId: string): Promise<PluginConnectionActionResult> {
|
||
return post("/api/plugins/connection-action", { config, actionId });
|
||
}
|
||
|
||
export async function notifyPlugin(pluginId: string, method: string, params: unknown = null): Promise<void> {
|
||
await post("/api/plugins/notify", { pluginId, method, params });
|
||
}
|
||
|
||
export async function sendPluginBinary(pluginId: string, channel: string, dataBase64: string): Promise<void> {
|
||
await post("/api/plugins/binary", { pluginId, channel, dataBase64 });
|
||
}
|
||
|
||
export async function listPluginFilesystemEntries(pluginId: string, providerId: string, options: { connectionId?: string; uri?: string; cursor?: string; limit?: number } = {}): Promise<PluginFilesystemListResult> {
|
||
return post("/api/plugins/filesystem/list", { pluginId, providerId, ...options });
|
||
}
|
||
|
||
export async function readPluginFilesystemFile(pluginId: string, providerId: string, uri: string, options: { connectionId?: string; maxBytes?: number } = {}): Promise<PluginFilesystemReadResult> {
|
||
return post("/api/plugins/filesystem/read", { pluginId, providerId, uri, ...options });
|
||
}
|
||
|
||
export async function writePluginFilesystemFile(pluginId: string, providerId: string, uri: string, dataBase64: string, options: { connectionId?: string; create?: boolean; overwrite?: boolean; etag?: string } = {}): Promise<PluginFilesystemMutationResult> {
|
||
return post("/api/plugins/filesystem/write", { pluginId, providerId, uri, dataBase64, create: options.create === true, overwrite: options.overwrite === true, connectionId: options.connectionId, etag: options.etag });
|
||
}
|
||
|
||
export async function createPluginFilesystemDirectory(pluginId: string, providerId: string, uri: string, connectionId?: string): Promise<PluginFilesystemMutationResult> {
|
||
return post("/api/plugins/filesystem/create-directory", { pluginId, providerId, uri, connectionId });
|
||
}
|
||
|
||
export async function deletePluginFilesystemEntry(pluginId: string, providerId: string, uri: string, options: { connectionId?: string; recursive?: boolean } = {}): Promise<PluginFilesystemMutationResult> {
|
||
return post("/api/plugins/filesystem/delete", { pluginId, providerId, uri, connectionId: options.connectionId, recursive: options.recursive === true });
|
||
}
|
||
|
||
export async function renamePluginFilesystemEntry(pluginId: string, providerId: string, sourceUri: string, targetUri: string, options: { connectionId?: string; overwrite?: boolean } = {}): Promise<PluginFilesystemMutationResult> {
|
||
return post("/api/plugins/filesystem/rename", { pluginId, providerId, sourceUri, targetUri, connectionId: options.connectionId, overwrite: options.overwrite === true });
|
||
}
|
||
|
||
export async function readPluginAsset(pluginId: string, path: string): Promise<PluginUiAssetPayload> {
|
||
const encodedPath = path
|
||
.split("/")
|
||
.map((part) => encodeURIComponent(part))
|
||
.join("/");
|
||
return pluginAssetPayload(`/api/plugins/${encodeURIComponent(pluginId)}/assets/${encodedPath}`);
|
||
}
|
||
|
||
export async function readPluginUiEntry(pluginId: string): Promise<PluginUiAssetPayload> {
|
||
return pluginAssetPayload(`/api/plugins/${encodeURIComponent(pluginId)}/ui`);
|
||
}
|
||
|
||
export async function readPluginUiAsset(pluginId: string, path: string): Promise<PluginUiAssetPayload> {
|
||
const encodedPath = path
|
||
.split("/")
|
||
.map((part) => encodeURIComponent(part))
|
||
.join("/");
|
||
return pluginAssetPayload(`/api/plugins/${encodeURIComponent(pluginId)}/ui/${encodedPath}`);
|
||
}
|
||
|
||
export async function subscribePluginEvents(onEvent: (event: PluginEvent) => void, onBinary?: (event: PluginBinaryEvent) => void): Promise<() => void> {
|
||
const source = new EventSource(apiUrl("/api/plugins/events"));
|
||
source.onmessage = (message) => {
|
||
const payload = JSON.parse(message.data) as ({ kind: "event" } & PluginEvent) | ({ kind: "binary" } & PluginBinaryEvent) | { kind: "lagged" };
|
||
if (payload.kind === "event") onEvent(payload);
|
||
if (payload.kind === "binary") onBinary?.(payload);
|
||
};
|
||
return () => source.close();
|
||
}
|
||
|
||
/**
|
||
* Subscribe to backend connection-liveness messages (#4339).
|
||
*
|
||
* The SSE payload is one `ConnectionLivenessMessage`: the plugin stream's `{ kind }` wrapper
|
||
* multiplexes two streams, whereas here `kind` is the message's own discriminator.
|
||
*/
|
||
export async function subscribeConnectionLiveness(onEvent: (event: ConnectionLivenessMessage) => void): Promise<() => void> {
|
||
const source = new EventSource(apiUrl("/api/connection/liveness-events"));
|
||
source.onmessage = (message) => {
|
||
onEvent(JSON.parse(message.data) as ConnectionLivenessMessage);
|
||
};
|
||
return () => source.close();
|
||
}
|
||
|
||
/**
|
||
* 「空闲自动断开」事件(issue #12112):后端在某个连接的连接池因空闲超过
|
||
* `idle_disconnect_secs` 被关闭时下发。与 liveness 一样走 SSE,载荷也是同一份
|
||
* `{ connectionId }`,因此前端在 desktop(Tauri 事件)与 web 两种传输上解析同一种结构。
|
||
*/
|
||
export async function subscribeConnectionIdleClosed(onEvent: (event: ConnectionIdleClosedEvent) => void): Promise<() => void> {
|
||
const source = new EventSource(apiUrl("/api/connection/idle-closed-events"));
|
||
source.onmessage = (message) => {
|
||
onEvent(JSON.parse(message.data) as ConnectionIdleClosedEvent);
|
||
};
|
||
return () => source.close();
|
||
}
|
||
|
||
async function pluginAssetPayload(path: string): Promise<PluginUiAssetPayload> {
|
||
const response = await fetch(apiUrl(path));
|
||
if (!response.ok) throw new Error(await response.text());
|
||
const bytes = new Uint8Array(await response.arrayBuffer());
|
||
let binary = "";
|
||
for (const byte of bytes) binary += String.fromCharCode(byte);
|
||
return {
|
||
contentType: response.headers.get("content-type") || "application/octet-stream",
|
||
dataBase64: btoa(binary),
|
||
etag: response.headers.get("etag") || "",
|
||
};
|
||
}
|
||
|
||
export async function listJdbcDrivers(): Promise<JdbcDriverInfo[]> {
|
||
return get("/api/jdbc/drivers");
|
||
}
|
||
|
||
export async function listJdbcMavenBundles(): Promise<JdbcMavenBundleInfo[]> {
|
||
return get("/api/jdbc/drivers/maven");
|
||
}
|
||
|
||
export async function listJdbcLocalBundles(): Promise<JdbcLocalBundleInfo[]> {
|
||
return get("/api/jdbc/drivers/local");
|
||
}
|
||
|
||
export async function importJdbcDrivers(pathsOrFiles: (string | File)[]): Promise<JdbcDriverInfo[]> {
|
||
const formData = new FormData();
|
||
for (const item of pathsOrFiles) {
|
||
if (item instanceof File) {
|
||
formData.append("files", item, item.name);
|
||
} else {
|
||
const fileName = item.split("/").pop() || "driver.jar";
|
||
const blob = await (await fetch(item)).blob();
|
||
formData.append("files", blob, fileName);
|
||
}
|
||
}
|
||
const res = await fetch(apiUrl("/api/jdbc/drivers"), {
|
||
method: "POST",
|
||
body: formData,
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
export async function installJdbcDriverFromMaven(coordinate: string, repositories: string[] = []): Promise<JdbcDriverInfo[]> {
|
||
return post("/api/jdbc/drivers/maven", { coordinate, repositories });
|
||
}
|
||
|
||
export async function installPrestoSqlJdbcDriver(): Promise<JdbcDriverInfo[]> {
|
||
return post("/api/jdbc/drivers/prestosql", {});
|
||
}
|
||
|
||
export async function deleteJdbcDriver(path: string): Promise<JdbcDriverInfo[]> {
|
||
const fileName = path.split("/").pop() || path;
|
||
return del(`/api/jdbc/drivers/${encodeURIComponent(fileName)}`);
|
||
}
|
||
|
||
export async function deleteJdbcMavenBundle(bundleId: string): Promise<JdbcDriverInfo[]> {
|
||
return del(`/api/jdbc/drivers/maven/${encodeURIComponent(bundleId)}`);
|
||
}
|
||
|
||
export async function deleteJdbcLocalBundle(bundleId: string): Promise<JdbcDriverInfo[]> {
|
||
return del(`/api/jdbc/drivers/local/${encodeURIComponent(bundleId)}`);
|
||
}
|
||
|
||
export async function jdbcPluginStatus(): Promise<JdbcPluginStatus> {
|
||
return get("/api/jdbc/plugin/status");
|
||
}
|
||
|
||
export async function installJdbcPlugin(): Promise<JdbcPluginStatus> {
|
||
return post("/api/jdbc/plugin/install", {});
|
||
}
|
||
|
||
export async function installJdbcPluginLocal(pathOrFile: string | File): Promise<JdbcPluginStatus> {
|
||
let blob: Blob;
|
||
let fileName: string;
|
||
if (pathOrFile instanceof File) {
|
||
blob = pathOrFile;
|
||
fileName = pathOrFile.name;
|
||
} else {
|
||
fileName = pathOrFile.split("/").pop() || "plugin.zip";
|
||
blob = await (await fetch(pathOrFile)).blob();
|
||
}
|
||
const formData = new FormData();
|
||
formData.append("file", blob, fileName);
|
||
const uploadRes = await fetch(apiUrl("/api/jdbc/plugin/install-local"), {
|
||
method: "POST",
|
||
body: formData,
|
||
});
|
||
if (!uploadRes.ok) throw await backendResponseError(uploadRes);
|
||
return uploadRes.json();
|
||
}
|
||
|
||
export async function uninstallJdbcPlugin(): Promise<JdbcPluginStatus> {
|
||
return post("/api/jdbc/plugin/uninstall", {});
|
||
}
|
||
|
||
export async function listInstalledAgentsLocal(): Promise<AgentDriverInfo[]> {
|
||
return get("/api/agents/installed-local");
|
||
}
|
||
|
||
export async function listInstalledAgents(_source?: UpdateDownloadSource): Promise<AgentDriverInfo[]> {
|
||
return get("/api/agents/installed");
|
||
}
|
||
|
||
export async function isAgentInstalled(dbType: string): Promise<boolean> {
|
||
return get(`/api/agents/installed/${encodeURIComponent(dbType)}`);
|
||
}
|
||
|
||
export async function getDriverStoreUsage(): Promise<DriverStoreUsage> {
|
||
return get("/api/agents/storage-usage");
|
||
}
|
||
|
||
export async function clearDriverDownloadCache(): Promise<void> {
|
||
await del("/api/agents/download-cache");
|
||
}
|
||
|
||
export async function getDriverRuntimeSummary(): Promise<DriverRuntimeSummary> {
|
||
return get("/api/agents/runtime");
|
||
}
|
||
|
||
export async function stopDriverRuntime(runtimeId: string): Promise<void> {
|
||
await post("/api/agents/runtime/stop", { runtimeId });
|
||
}
|
||
|
||
export async function restartDriverRuntime(runtimeId: string): Promise<void> {
|
||
await post("/api/agents/runtime/restart", { runtimeId });
|
||
}
|
||
|
||
export async function installAgent(dbType: string, _source?: UpdateDownloadSource, operationId?: string): Promise<void> {
|
||
await post("/api/agents/install", { dbType, operationId });
|
||
}
|
||
|
||
export async function upgradeAllAgents(_source?: UpdateDownloadSource, operationId?: string): Promise<UpgradeAllAgentDriversResult> {
|
||
return post("/api/agents/upgrade-all", { operationId });
|
||
}
|
||
|
||
export async function cancelAgentInstall(dbType: string, operationId?: string): Promise<void> {
|
||
await post("/api/agents/cancel-install", { dbType, operationId });
|
||
}
|
||
|
||
export async function cancelAgentUpgradeAll(operationId?: string): Promise<void> {
|
||
await post("/api/agents/cancel-upgrade-all", { operationId });
|
||
}
|
||
|
||
export async function checkAgentUpdateBlockers(dbTypes: string[]): Promise<AgentUpdateBlocker[]> {
|
||
return post("/api/agents/update-blockers", { dbTypes });
|
||
}
|
||
|
||
export async function uninstallAgent(dbType: string): Promise<void> {
|
||
await post("/api/agents/uninstall", { dbType });
|
||
}
|
||
|
||
export async function getAgentJavaRuntimeConfig(): Promise<JavaRuntimeConfig> {
|
||
return get("/api/agents/java-runtime");
|
||
}
|
||
|
||
export async function setAgentJavaRuntimeConfig(config: JavaRuntimeConfig): Promise<JavaRuntimeConfig> {
|
||
return post("/api/agents/java-runtime", { config });
|
||
}
|
||
|
||
export async function invalidateAgentRegistryCache(): Promise<void> {
|
||
await post("/api/agents/invalidate-registry-cache", {});
|
||
}
|
||
|
||
export async function importAgentsFromZip(fileOrPath: string | File, operationId?: string): Promise<AgentOfflineImportResult> {
|
||
if (typeof fileOrPath === "string") {
|
||
throw new Error("Offline package import in web mode requires a File object, not a file path");
|
||
}
|
||
const formData = new FormData();
|
||
if (operationId) formData.append("operationId", operationId);
|
||
formData.append("file", fileOrPath);
|
||
const res = await fetch(apiUrl("/api/agents/import-offline"), {
|
||
method: "POST",
|
||
body: formData,
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
const result: AgentOfflineImportResult = await res.json();
|
||
return { count: result.count, jreCount: result.jreCount ?? 0, failures: result.failures ?? [] };
|
||
}
|
||
|
||
export async function previewAgentOfflineExport(): Promise<AgentOfflineExportPreview> {
|
||
throw new Error("Offline Agent package export is only available in the desktop app.");
|
||
}
|
||
|
||
export async function exportAgentsOffline(_path: string, _driverKeys: string[]): Promise<AgentOfflineExportResult> {
|
||
throw new Error("Offline Agent package export is only available in the desktop app.");
|
||
}
|
||
|
||
export async function listUserSkills(_settings: UserSkillRootSettings): Promise<UserSkillsListResult> {
|
||
throw new Error("AI skills are only available in the desktop app.");
|
||
}
|
||
|
||
export async function readUserSkills(_ids: string[], _settings: UserSkillRootSettings): Promise<UserSkillsReadResult> {
|
||
throw new Error("AI skills are only available in the desktop app.");
|
||
}
|
||
|
||
export async function importAgentDriver(dbType: string, pathOrFile: string | File): Promise<void> {
|
||
let blob: Blob;
|
||
let fileName: string;
|
||
if (pathOrFile instanceof File) {
|
||
blob = pathOrFile;
|
||
fileName = pathOrFile.name;
|
||
} else {
|
||
fileName = pathOrFile.split("/").pop() || "agent";
|
||
blob = await (await fetch(pathOrFile)).blob();
|
||
}
|
||
const formData = new FormData();
|
||
formData.append("dbType", dbType);
|
||
formData.append("file", blob, fileName);
|
||
const uploadRes = await fetch(apiUrl("/api/agents/import-driver"), {
|
||
method: "POST",
|
||
body: formData,
|
||
});
|
||
if (!uploadRes.ok) throw await backendResponseError(uploadRes);
|
||
}
|
||
|
||
export const importAgentJar = importAgentDriver;
|
||
|
||
export async function reinstallJre(jreKey?: string, _source?: UpdateDownloadSource, operationId?: string): Promise<void> {
|
||
await post("/api/agents/reinstall-jre", { jreKey, operationId });
|
||
}
|
||
|
||
export async function uninstallJre(jreKey: string): Promise<void> {
|
||
await post("/api/agents/uninstall-jre", { jreKey });
|
||
}
|
||
|
||
export async function listenAgentInstallProgress(handler: (progress: DriverInstallProgress) => void): Promise<() => void> {
|
||
const es = new EventSource(apiUrl("/api/agents/progress/global"));
|
||
es.onmessage = (event) => {
|
||
try {
|
||
handler(JSON.parse(event.data));
|
||
} catch {
|
||
/* ignore malformed progress events */
|
||
}
|
||
};
|
||
return () => es.close();
|
||
}
|
||
|
||
export async function loadSavedSqlLibrary(): Promise<SavedSqlLibrary> {
|
||
return get("/api/saved-sql");
|
||
}
|
||
|
||
export async function loadSavedSqlFilesForSync(): Promise<SavedSqlFile[]> {
|
||
throw new Error("SQL directory sync is only available in the desktop app.");
|
||
}
|
||
|
||
export async function loadSavedSqlFile(id: string): Promise<SavedSqlFile | null> {
|
||
return get(`/api/saved-sql/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
export async function saveSavedSqlFolder(folder: SavedSqlFolder): Promise<SavedSqlFolder> {
|
||
return post("/api/saved-sql/folders", folder);
|
||
}
|
||
|
||
export async function deleteSavedSqlFolder(id: string): Promise<void> {
|
||
return del(`/api/saved-sql/folders/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
export async function saveSavedSqlFile(file: SavedSqlFile): Promise<SavedSqlFile> {
|
||
return post("/api/saved-sql", file);
|
||
}
|
||
|
||
export async function saveDefaultQueryContent(id: string, sql: string, updatedAt: string): Promise<void> {
|
||
return post(`/api/saved-sql/${encodeURIComponent(id)}/default-content`, { sql, updatedAt });
|
||
}
|
||
|
||
export async function deleteSavedSqlFile(id: string): Promise<void> {
|
||
return del(`/api/saved-sql/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
export async function savedSqlStorageDir(): Promise<string> {
|
||
return "";
|
||
}
|
||
|
||
export async function openSavedSqlStorageDir(_dir?: string | null): Promise<void> {
|
||
throw new Error("SQL storage directory is only available in the desktop app.");
|
||
}
|
||
|
||
export async function revealPathInFileManager(_path: string): Promise<void> {
|
||
throw new Error("Reveal in file manager is only available in the desktop app.");
|
||
}
|
||
|
||
export async function deleteDatabaseBackupFiles(_paths: string[], _allowedRoots: string[] = []): Promise<number> {
|
||
throw new Error("Database backup file management is only available in the desktop app.");
|
||
}
|
||
|
||
export async function isSqliteDatabaseFile(_path: string): Promise<boolean> {
|
||
return false;
|
||
}
|
||
|
||
export async function backupSqliteDatabase(_connectionId: string, _destinationPath: string): Promise<void> {
|
||
throw new Error("SQLite backup is only available in the desktop app.");
|
||
}
|
||
|
||
export async function restoreSqliteDatabase(_connectionId: string, _sourcePath: string): Promise<void> {
|
||
throw new Error("Remote SQLite restore is only available in the desktop app.");
|
||
}
|
||
|
||
export async function syncSavedSqlDirectory(_request: SavedSqlSyncRequest): Promise<void> {
|
||
throw new Error("SQL directory sync is only available in the desktop app.");
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Schema
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function listDatabases(connectionId: string): Promise<DatabaseInfo[]> {
|
||
return get(`/api/schema/databases?${qs({ connection_id: connectionId })}`);
|
||
}
|
||
|
||
export async function listDatabaseMetadata(connectionId: string): Promise<DatabaseInfo[]> {
|
||
return get(`/api/schema/database-metadata?${qs({ connection_id: connectionId })}`);
|
||
}
|
||
|
||
export async function listDatabaseStorage(connectionId: string, databases: string[]): Promise<DatabaseStorageInfo[]> {
|
||
return post("/api/schema/database-storage", {
|
||
connection_id: connectionId,
|
||
databases,
|
||
});
|
||
}
|
||
|
||
export async function listXuguTablespaces(connectionId: string, database?: string): Promise<XuguTablespaceInfo[]> {
|
||
return get(`/api/schema/xugu/tablespaces?${qs({ connection_id: connectionId, database })}`);
|
||
}
|
||
|
||
export async function getSqlServerCompletionContext(connectionId: string, database: string): Promise<SqlServerCompletionContext> {
|
||
return get(`/api/schema/sqlserver/completion-context?${qs({ connection_id: connectionId, database })}`);
|
||
}
|
||
|
||
export async function listDorisCatalogs(connectionId: string): Promise<CatalogInfo[]> {
|
||
return get(`/api/schema/doris/catalogs?${qs({ connection_id: connectionId })}`);
|
||
}
|
||
|
||
export async function listDorisCatalogDatabases(connectionId: string, catalog: string): Promise<DatabaseInfo[]> {
|
||
return get(`/api/schema/doris/catalog-databases?${qs({ connection_id: connectionId, catalog })}`);
|
||
}
|
||
|
||
export async function listSqlServerLinkedServers(connectionId: string): Promise<LinkedServerInfo[]> {
|
||
return get(`/api/schema/sqlserver/linked-servers?${qs({ connection_id: connectionId })}`);
|
||
}
|
||
|
||
export async function listSqlServerLinkedServerCatalogs(connectionId: string, server: string): Promise<DatabaseInfo[]> {
|
||
return get(`/api/schema/sqlserver/linked-server-catalogs?${qs({ connection_id: connectionId, server })}`);
|
||
}
|
||
|
||
export async function listSqlServerLinkedServerSchemas(connectionId: string, server: string, catalog: string): Promise<string[]> {
|
||
return get(`/api/schema/sqlserver/linked-server-schemas?${qs({ connection_id: connectionId, server, catalog })}`);
|
||
}
|
||
|
||
export async function listSqlServerLinkedServerTables(connectionId: string, server: string, catalog: string, schema: string, filter?: string, limit?: number, offset?: number): Promise<TableInfo[]> {
|
||
return get(`/api/schema/sqlserver/linked-server-tables?${qs({ connection_id: connectionId, server, catalog, schema, filter, limit, offset })}`);
|
||
}
|
||
|
||
export async function saveSchemaCache(cacheKey: string, payload: unknown): Promise<void> {
|
||
return post("/api/schema/cache", { cacheKey, payload });
|
||
}
|
||
|
||
export async function loadSchemaCache<T = unknown>(cacheKey: string): Promise<T | null> {
|
||
return get(`/api/schema/cache?${qs({ cache_key: cacheKey })}`);
|
||
}
|
||
|
||
export async function deleteSchemaCachePrefix(prefix: string): Promise<void> {
|
||
return del(`/api/schema/cache-prefix?${qs({ prefix })}`);
|
||
}
|
||
|
||
export async function listSchemas(connectionId: string, database: string, applyVisibleFilter = false): Promise<string[]> {
|
||
return get(`/api/schema/schemas?${qs({ connection_id: connectionId, database, apply_visible_filter: applyVisibleFilter || undefined })}`);
|
||
}
|
||
|
||
export async function listSchemaInfos(connectionId: string, database: string): Promise<SchemaInfo[]> {
|
||
const schemas = await listSchemas(connectionId, database);
|
||
return schemas.map((name) => ({ name, comment: null }));
|
||
}
|
||
|
||
export async function listTables(connectionId: string, database: string, schema: string, filter?: string, limit?: number, offset?: number, objectTypes?: SidebarObjectKind[], catalog?: string, tableNameFilter?: TableNameFilter, exactIdentity?: boolean): Promise<TableInfo[]> {
|
||
return get(`/api/schema/tables?${qs({ connection_id: connectionId, database, schema, filter, limit, offset, object_types: objectTypes?.join(","), catalog, table_name_filter: tableNameFilter ? JSON.stringify(tableNameFilter) : undefined, exact_identity: exactIdentity || undefined })}`);
|
||
}
|
||
|
||
export async function getTableComment(connectionId: string, database: string, schema: string, table: string, catalog?: string, exactIdentity?: boolean): Promise<string | null> {
|
||
return get(`/api/schema/table-comment?${qs({ connection_id: connectionId, database, schema, table, catalog, exact_identity: exactIdentity || undefined })}`);
|
||
}
|
||
|
||
export async function getMysqlTableAutoIncrement(connectionId: string, database: string, table: string): Promise<string | null> {
|
||
return get(`/api/schema/mysql/auto-increment?${qs({ connection_id: connectionId, database, table })}`);
|
||
}
|
||
|
||
export async function listObjects(
|
||
connectionId: string,
|
||
database: string,
|
||
schema: string,
|
||
objectTypes?: (SidebarObjectKind | "EVENT")[],
|
||
filter?: string,
|
||
limit?: number,
|
||
offset?: number,
|
||
catalog?: string,
|
||
tableNameFilter?: TableNameFilter,
|
||
executionId?: string,
|
||
exactIdentity?: boolean,
|
||
): Promise<ObjectInfo[]> {
|
||
return get(
|
||
`/api/schema/objects?${qs({
|
||
connection_id: connectionId,
|
||
database,
|
||
schema,
|
||
object_types: objectTypes?.join(","),
|
||
filter,
|
||
limit,
|
||
offset,
|
||
catalog,
|
||
table_name_filter: tableNameFilter ? JSON.stringify(tableNameFilter) : undefined,
|
||
execution_id: executionId,
|
||
exact_identity: exactIdentity || undefined,
|
||
})}`,
|
||
);
|
||
}
|
||
|
||
export async function listObjectStatistics(connectionId: string, database: string, schema: string): Promise<ObjectStatistics[]> {
|
||
return get(`/api/schema/object-statistics?${qs({ connection_id: connectionId, database, schema })}`);
|
||
}
|
||
|
||
export async function listCompletionObjects(connectionId: string, database: string, schema: string): Promise<ObjectInfo[]> {
|
||
return get(`/api/schema/completion-objects?${qs({ connection_id: connectionId, database, schema })}`);
|
||
}
|
||
|
||
export async function completionAssistantSearch(request: CompletionAssistantRequest): Promise<CompletionAssistantResponse> {
|
||
return post("/api/schema/completion-assistant", request);
|
||
}
|
||
|
||
export async function getObjectSource(connectionId: string, database: string, schema: string, name: string, objectType: ObjectSourceKind, signature?: string, relationName?: string): Promise<ObjectSource> {
|
||
return get(`/api/schema/object-source?${qs({ connection_id: connectionId, database, schema, table: name, object_type: objectType, signature, relation_name: relationName })}`);
|
||
}
|
||
|
||
export async function getEventInfo(connectionId: string, database: string, schema: string, name: string): Promise<MysqlEventInfo> {
|
||
return get(`/api/schema/event-info?${qs({ connection_id: connectionId, database, schema, table: name })}`);
|
||
}
|
||
|
||
export async function getCustomTypeDetails(connectionId: string, database: string, schema: string, name: string): Promise<CustomTypeDetails> {
|
||
return get(`/api/schema/custom-type-details?${qs({ connection_id: connectionId, database, schema, table: name })}`);
|
||
}
|
||
|
||
export async function getOracleTypeManagementSnapshot(connectionId: string, database: string, owner: string, name: string, knownObjects: OracleTypeManagementObjectKey[] = []): Promise<OracleTypeManagementSnapshot> {
|
||
if (knownObjects.length) return post("/api/schema/oracle-type-management-snapshot", { target: { connectionId, database, owner, name }, knownObjects });
|
||
return get(`/api/schema/oracle-type-management-snapshot?${qs({ connection_id: connectionId, database, schema: owner, table: name })}`);
|
||
}
|
||
export function buildOracleTypeManagementSql(input: OracleTypeManagementSqlInput): Promise<OracleTypeManagementSqlResult> {
|
||
return post("/api/query/build-oracle-type-management-sql", input);
|
||
}
|
||
export function verifyOracleTypeData(input: OracleTypeDataRequest): Promise<OracleTypeDataResult> {
|
||
return post("/api/schema/oracle-type-data", input);
|
||
}
|
||
export function prepareOracleTypeEvolution(input: OracleTypeEvolutionRequest): Promise<OracleTypeEvolutionManifest> {
|
||
return post("/api/schema/oracle-type-evolution", input);
|
||
}
|
||
|
||
export async function getColumns(connectionId: string, database: string, schema: string, table: string, catalog?: string, clientSessionId?: string, currentSchema?: string, exactIdentity?: boolean): Promise<ColumnInfo[]> {
|
||
return get(`/api/schema/columns?${qs({ connection_id: connectionId, database, schema, table, catalog, client_session_id: clientSessionId, current_schema: currentSchema, exact_identity: exactIdentity || undefined })}`);
|
||
}
|
||
|
||
export async function getPluginTableMetadata(request: PluginTableMetadataRequest): Promise<PluginTableMetadata> {
|
||
return post("/api/plugin/table-metadata", request);
|
||
}
|
||
|
||
export async function getSqlServerColumnMetadata(connectionId: string, database: string, schema: string, table: string): Promise<SqlServerColumnMetadata[]> {
|
||
return get(`/api/schema/sqlserver/column-metadata?${qs({ connection_id: connectionId, database, schema, table })}`);
|
||
}
|
||
|
||
export interface TableColumnsResult {
|
||
table_name: string;
|
||
columns: ColumnInfo[];
|
||
error?: string;
|
||
}
|
||
|
||
export async function getAllColumns(connectionId: string, database: string, schema: string): Promise<TableColumnsResult[]> {
|
||
return get(`/api/schema/all-columns?${qs({ connection_id: connectionId, database, schema })}`);
|
||
}
|
||
|
||
export async function listDataTypes(connectionId: string, database: string): Promise<string[]> {
|
||
return get(`/api/schema/data-types?${qs({ connection_id: connectionId, database })}`);
|
||
}
|
||
|
||
export async function listIndexes(connectionId: string, database: string, schema: string, table: string, catalog?: string, exactIdentity?: boolean): Promise<IndexInfo[]> {
|
||
return get(`/api/schema/indexes?${qs({ connection_id: connectionId, database, schema, table, catalog, exact_identity: exactIdentity || undefined })}`);
|
||
}
|
||
|
||
export async function listReferenceKeyColumns(connectionId: string, database: string, schema: string, table: string, catalog?: string): Promise<string[]> {
|
||
return get(`/api/schema/reference-key-columns?${qs({ connection_id: connectionId, database, schema, table, catalog })}`);
|
||
}
|
||
|
||
export async function listReferenceKeys(connectionId: string, database: string, schema: string, table: string, catalog?: string): Promise<ReferenceKeyInfo[]> {
|
||
return get(`/api/schema/reference-keys?${qs({ connection_id: connectionId, database, schema, table, catalog })}`);
|
||
}
|
||
|
||
export async function listForeignKeys(connectionId: string, database: string, schema: string, table: string, catalog?: string, exactIdentity?: boolean): Promise<ForeignKeyInfo[]> {
|
||
return get(`/api/schema/foreign-keys?${qs({ connection_id: connectionId, database, schema, table, catalog, exact_identity: exactIdentity || undefined })}`);
|
||
}
|
||
|
||
export async function listForeignKeysForDatabase(connectionId: string, database: string, schema: string, catalog?: string, executionId?: string): Promise<Record<string, ForeignKeyInfo[]>> {
|
||
return get(`/api/schema/foreign-keys-for-database?${qs({ connection_id: connectionId, database, schema, catalog, execution_id: executionId })}`);
|
||
}
|
||
|
||
export async function listTriggers(connectionId: string, database: string, schema: string, table: string, catalog?: string, exactIdentity?: boolean): Promise<TriggerInfo[]> {
|
||
return get(`/api/schema/triggers?${qs({ connection_id: connectionId, database, schema, table, catalog, exact_identity: exactIdentity || undefined })}`);
|
||
}
|
||
|
||
export async function listConstraints(connectionId: string, database: string, schema: string, table: string, catalog?: string, exactIdentity?: boolean): Promise<ConstraintInfo[]> {
|
||
return get(`/api/schema/constraints?${qs({ connection_id: connectionId, database, schema, table, catalog, exact_identity: exactIdentity || undefined })}`);
|
||
}
|
||
|
||
export async function listPartitions(connectionId: string, database: string, schema: string, table: string, catalog?: string): Promise<PartitionInfo[]> {
|
||
return get(`/api/schema/partitions?${qs({ connection_id: connectionId, database, schema, table, catalog })}`);
|
||
}
|
||
|
||
export interface TablePartitionStatus {
|
||
isPartitionedParent: boolean;
|
||
isPartition: boolean;
|
||
isForeign: boolean;
|
||
}
|
||
|
||
export async function getTablePartitionStatus(connectionId: string, database: string, schema: string, table: string): Promise<TablePartitionStatus> {
|
||
return get(`/api/schema/table-partition-status?${qs({ connection_id: connectionId, database, schema, table })}`);
|
||
}
|
||
|
||
export async function getTablePartitioning(connectionId: string, database: string, schema: string, table: string): Promise<import("@/types/database").PgTablePartitioning> {
|
||
return get(`/api/schema/table-partitioning?${qs({ connection_id: connectionId, database, schema, table })}`);
|
||
}
|
||
|
||
export async function listInvalidIndexes(connectionId: string, database: string, schema: string, table: string): Promise<string[]> {
|
||
return get(`/api/schema/invalid-indexes?${qs({ connection_id: connectionId, database, schema, table })}`);
|
||
}
|
||
|
||
export async function listSubpartitions(connectionId: string, database: string, schema: string, table: string, catalog?: string): Promise<SubpartitionInfo[]> {
|
||
return get(`/api/schema/subpartitions?${qs({ connection_id: connectionId, database, schema, table, catalog })}`);
|
||
}
|
||
|
||
export async function getTableDdl(connectionId: string, database: string, schema: string, table: string, objectType?: ObjectSourceKind, catalog?: string, portable = false, exactIdentity?: boolean): Promise<string> {
|
||
return get(`/api/schema/ddl?${qs({ connection_id: connectionId, database, schema, table, object_type: objectType, catalog, portable, exact_identity: exactIdentity || undefined })}`);
|
||
}
|
||
|
||
export async function getTableDisplayDdl(connectionId: string, database: string, schema: string, table: string, objectType?: ObjectSourceKind, catalog?: string): Promise<string> {
|
||
return get(`/api/schema/ddl?${qs({ connection_id: connectionId, database, schema, table, object_type: objectType, catalog, include_postgres_access: true })}`);
|
||
}
|
||
|
||
export async function prepareSchemaDiff(options: SchemaDiffPreparationOptions): Promise<SchemaDiffPreparation> {
|
||
return schemaDiffPreparationFromWire(await post<SchemaDiffPreparationWire>("/api/schema-diff/prepare", schemaDiffPreparationToWire(options)));
|
||
}
|
||
|
||
export async function generateSchemaSyncSql(diffs: TableDiff[], databaseType: DatabaseType, targetSchema?: string, functionDiffs?: FunctionDiff[], sequenceDiffs?: SequenceDiff[], ruleDiffs?: RuleDiff[], ownerDiffs?: OwnerDiff[], cascadeDelete?: boolean): Promise<string> {
|
||
return post("/api/schema-diff/generate-sync-sql", {
|
||
diffs,
|
||
databaseType,
|
||
targetSchema,
|
||
functionDiffs: functionDiffsToWire(functionDiffs ?? []),
|
||
sequenceDiffs: sequenceDiffs ?? [],
|
||
ruleDiffs: ruleDiffs ?? [],
|
||
ownerDiffs: ownerDiffs ?? [],
|
||
cascadeDelete: cascadeDelete ?? false,
|
||
});
|
||
}
|
||
|
||
export async function generateSchemaSyncPlan(input: SelectedSchemaDiffInput, options: GenerateSchemaSyncPlanOptions): Promise<SchemaSyncSqlPlan> {
|
||
return post("/api/schema-diff/generate-sync-plan", {
|
||
...input,
|
||
...options,
|
||
functionDiffs: functionDiffsToWire(input.functionDiffs),
|
||
});
|
||
}
|
||
|
||
export async function listFunctions(connectionId: string, database: string, schema: string): Promise<FunctionInfo[]> {
|
||
return (await get<FunctionInfoWire[]>(`/api/schema/functions?${qs({ connection_id: connectionId, database, schema })}`)).map(functionInfoFromWire);
|
||
}
|
||
|
||
export function loadSchemaDiffRoutines(connectionId: string, database: string, schema: string, selectedRoutines?: string[]): Promise<OracleRoutineSnapshot> {
|
||
return get(`/api/schema/diff-routines?${qs({ connection_id: connectionId, database, schema, selected_routines: selectedRoutines === undefined ? undefined : JSON.stringify(selectedRoutines) })}`);
|
||
}
|
||
|
||
export function prepareOracleRoutineSave(input: OracleRoutineSaveInput): Promise<OracleRoutineOperation> {
|
||
return post("/api/schema/routine-save", input);
|
||
}
|
||
|
||
export function prepareOracleRoutinePlan(input: OracleRoutinePlanInput): Promise<OracleRoutinePlan> {
|
||
return post("/api/schema/routine-plan", input);
|
||
}
|
||
|
||
export function prepareOracleRoutineRecovery(operationId: string): Promise<OracleRoutinePlan> {
|
||
return post("/api/schema/routine-recovery", { operationId });
|
||
}
|
||
|
||
export function executeOracleRoutineOperation(operationId: string, approvedSql: string): Promise<OracleRoutineOperation> {
|
||
return post("/api/schema/routine-execute", { operationId, approvedSql });
|
||
}
|
||
|
||
export function observeOracleRoutineOperation(operationId: string, refreshReadback = false): Promise<OracleRoutineOperationObservation> {
|
||
return get(`/api/schema/routine-operation?${qs({ operation_id: operationId, refresh_readback: refreshReadback })}`);
|
||
}
|
||
|
||
export function updateOracleRoutineOperationReferences(retainOperationIds: string[], releaseOperationIds: string[]): Promise<void> {
|
||
return post("/api/schema/routine-operation-references", { retainOperationIds, releaseOperationIds });
|
||
}
|
||
|
||
export async function listSequences(connectionId: string, database: string, schema: string, withLastValues: boolean): Promise<SequenceInfo[]> {
|
||
return get(`/api/schema/sequences?${qs({ connection_id: connectionId, database, schema, with_last_values: withLastValues })}`);
|
||
}
|
||
|
||
export async function listRules(connectionId: string, database: string, schema: string): Promise<RuleInfo[]> {
|
||
return get(`/api/schema/rules?${qs({ connection_id: connectionId, database, schema })}`);
|
||
}
|
||
|
||
export async function listOwners(connectionId: string, database: string, schema: string): Promise<OwnerInfo[]> {
|
||
return get(`/api/schema/owners?${qs({ connection_id: connectionId, database, schema })}`);
|
||
}
|
||
|
||
export async function getTableOwner(connectionId: string, database: string, schema: string, table: string): Promise<string | null> {
|
||
return get(`/api/schema/table-owner?${qs({ connection_id: connectionId, database, schema, table })}`);
|
||
}
|
||
|
||
export async function listExtensions(connectionId: string, database: string, schema?: string): Promise<ExtensionInfo[]> {
|
||
return get(`/api/schema/extensions?${qs({ connection_id: connectionId, database, schema })}`);
|
||
}
|
||
|
||
export async function listAvailableExtensions(connectionId: string, database: string): Promise<ExtensionInfo[]> {
|
||
return get(`/api/schema/available-extensions?${qs({ connection_id: connectionId, database })}`);
|
||
}
|
||
|
||
export async function listEventTriggers(connectionId: string, database: string): Promise<EventTriggerInfo[]> {
|
||
return get(`/api/schema/event-triggers?${qs({ connection_id: connectionId, database })}`);
|
||
}
|
||
|
||
export async function listDialectDataTypes(dialectName: string): Promise<string[]> {
|
||
return get(`/api/dialect/data-types?${qs({ dialect_name: dialectName })}`);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Docs
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function collectDocsSnapshot(connectionId: string, database: string, schemas: string[], tables: string[], projectName?: string): Promise<SchemaSnapshot> {
|
||
return post("/api/docs/snapshot", { connectionId, database, schemas, tables, projectName });
|
||
}
|
||
|
||
// HTTP snapshot transport is request/response; its progress remains indeterminate.
|
||
export async function collectDocsSnapshotForExport(connectionId: string, database: string, schemas: string[], tables: string[], _onProgress: (progress: import("./tauri").DocsCollectProgress) => void): Promise<SchemaSnapshot> {
|
||
return post("/api/docs/snapshot", { connectionId, database, schemas, tables, projectName: database, maxConcurrentTables: 2 });
|
||
}
|
||
|
||
export async function loadDocsAnnotations(connectionId: string): Promise<AnnotationFile | null> {
|
||
return post("/api/docs/annotations/load", { connectionId });
|
||
}
|
||
|
||
export async function applyDocsAnnotations(connectionId: string, snapshot: SchemaSnapshot, annotations: AnnotationFile): Promise<SchemaSnapshot> {
|
||
return post("/api/docs/annotations/apply", { connectionId, snapshot, annotations });
|
||
}
|
||
|
||
export async function saveDocsAnnotations(connectionId: string, annotations: AnnotationFile): Promise<void> {
|
||
return post("/api/docs/annotations/save", { connectionId, annotations });
|
||
}
|
||
|
||
export async function exportDocsHtml(filePath: string, snapshot: SchemaSnapshot, annotations: AnnotationFile, lang: string): Promise<void> {
|
||
const result = await post<{ content: string }>("/api/docs/export", { snapshot, annotations, lang });
|
||
// No `downloadTextFile` here: it prepends a BOM, which the Tauri command's
|
||
// `std::fs::write(&file_path, html)` does not. The two callers must produce
|
||
// byte-identical output for the same inputs.
|
||
const fileName = filePath.split(/[\\/]/).pop() || "docs.html";
|
||
const blob = new Blob([result.content], { type: "text/html;charset=utf-8" });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = fileName;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Query
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function executeQuery(
|
||
connectionId: string,
|
||
database: string,
|
||
sql: string,
|
||
schema?: string,
|
||
executionId?: string,
|
||
options?: {
|
||
maxRows?: number;
|
||
catalog?: string;
|
||
fetchSize?: number;
|
||
pageSize?: number;
|
||
rowOffset?: number;
|
||
resultSessionId?: string;
|
||
clientSessionId?: string;
|
||
timeoutSecs?: number;
|
||
executionMode?: "simple" | "postgres_read_only_transaction";
|
||
},
|
||
): Promise<QueryResult> {
|
||
return post("/api/query/execute", {
|
||
connectionId,
|
||
database,
|
||
sql,
|
||
schema,
|
||
executionId,
|
||
...options,
|
||
});
|
||
}
|
||
|
||
export async function executeConditionalUpdate(
|
||
connectionId: string,
|
||
database: string,
|
||
sql: string,
|
||
schema?: string,
|
||
executionId?: string,
|
||
options?: {
|
||
maxRows?: number;
|
||
catalog?: string;
|
||
fetchSize?: number;
|
||
pageSize?: number;
|
||
rowOffset?: number;
|
||
resultSessionId?: string;
|
||
clientSessionId?: string;
|
||
timeoutSecs?: number;
|
||
executionMode?: "simple" | "postgres_read_only_transaction";
|
||
typeRecovery?: import("@/types/database").TypeRecoveryContext;
|
||
},
|
||
): Promise<QueryResult> {
|
||
return post("/api/query/execute-conditional-update", {
|
||
connectionId,
|
||
database,
|
||
sql,
|
||
schema,
|
||
executionId,
|
||
...options,
|
||
});
|
||
}
|
||
|
||
export async function executeMulti(
|
||
connectionId: string,
|
||
database: string,
|
||
sql: string,
|
||
schema?: string,
|
||
executionId?: string,
|
||
options?: {
|
||
maxRows?: number;
|
||
catalog?: string;
|
||
fetchSize?: number;
|
||
pageSize?: number;
|
||
rowOffset?: number;
|
||
maxResultBytes?: number;
|
||
resultKeyColumns?: string[];
|
||
tableDataPreview?: boolean;
|
||
deferLobs?: boolean;
|
||
resultSessionId?: string;
|
||
clientSessionId?: string;
|
||
timeoutSecs?: number;
|
||
useTransaction?: boolean;
|
||
continueOnError?: boolean;
|
||
executionMode?: "simple";
|
||
/** MySQL auto-commit tabs: keep a transaction the user opened explicitly
|
||
* (`BEGIN` / `START TRANSACTION`) open across executions until COMMIT /
|
||
* ROLLBACK instead of rolling it back when the batch ends. */
|
||
preserveExplicitTransaction?: boolean;
|
||
},
|
||
): Promise<QueryResult[]> {
|
||
return postQueryWithDiagnostics(
|
||
"/api/query/execute-multi",
|
||
{
|
||
connectionId,
|
||
database,
|
||
sql,
|
||
schema,
|
||
executionId,
|
||
...options,
|
||
},
|
||
executionId,
|
||
);
|
||
}
|
||
|
||
export interface ExecuteMultiProgress {
|
||
executionId: string;
|
||
statementIndex: number;
|
||
completed: number;
|
||
total: number;
|
||
success: boolean;
|
||
executionTimeMs: number;
|
||
affectedRows: number;
|
||
error?: BackendError;
|
||
}
|
||
|
||
export async function subscribeQueryMessages(_executionId: string, _onMessages: (messages: import("@/types/database").QueryMessage[]) => void): Promise<() => void> {
|
||
return () => {};
|
||
}
|
||
|
||
export async function executeMultiWithProgress(
|
||
connectionId: string,
|
||
database: string,
|
||
sql: string,
|
||
onProgress: (progress: ExecuteMultiProgress) => void,
|
||
schema?: string,
|
||
options?: {
|
||
maxRows?: number;
|
||
catalog?: string;
|
||
fetchSize?: number;
|
||
pageSize?: number;
|
||
rowOffset?: number;
|
||
maxResultBytes?: number;
|
||
resultKeyColumns?: string[];
|
||
tableDataPreview?: boolean;
|
||
deferLobs?: boolean;
|
||
resultSessionId?: string;
|
||
clientSessionId?: string;
|
||
timeoutSecs?: number;
|
||
useTransaction?: boolean;
|
||
continueOnError?: boolean;
|
||
executionMode?: "simple";
|
||
preserveExplicitTransaction?: boolean;
|
||
executionId?: string;
|
||
},
|
||
): Promise<QueryResult[]> {
|
||
const executionId = options?.executionId ?? uuid();
|
||
const { executionId: _executionId, ...executeOptions } = options ?? {};
|
||
const results = await executeMulti(connectionId, database, sql, schema, executionId, executeOptions);
|
||
const total = results.length;
|
||
results.forEach((result, index) => {
|
||
const statementIndex = result.statement_index ?? index;
|
||
const success = result.execution_error !== true;
|
||
onProgress({
|
||
executionId,
|
||
statementIndex,
|
||
completed: index + 1,
|
||
total,
|
||
success,
|
||
executionTimeMs: result.execution_time_ms,
|
||
affectedRows: result.affected_rows,
|
||
error: success ? undefined : result.error,
|
||
});
|
||
});
|
||
return results;
|
||
}
|
||
|
||
export interface LargeValueRequest {
|
||
connectionId: string;
|
||
database: string;
|
||
valueRef: string;
|
||
offset?: number;
|
||
limit?: number;
|
||
executionId?: string;
|
||
clientSessionId?: string;
|
||
catalog?: string;
|
||
txnSessionId?: string;
|
||
downloadEncoding?: "binary" | "utf8" | "gbk";
|
||
}
|
||
|
||
export interface LargeValueChunk {
|
||
status: string;
|
||
data: string;
|
||
next_offset: number;
|
||
eof: boolean;
|
||
value_kind: "text" | "binary";
|
||
}
|
||
|
||
export async function readLargeValueChunk(request: LargeValueRequest): Promise<LargeValueChunk> {
|
||
return post("/api/query/large-value/chunk", request);
|
||
}
|
||
|
||
export async function releaseLargeValue(request: LargeValueRequest): Promise<boolean> {
|
||
return post("/api/query/large-value/release", request);
|
||
}
|
||
|
||
export async function downloadLargeValue(request: LargeValueRequest, filePath: string, signal?: AbortSignal): Promise<void> {
|
||
signal?.throwIfAborted();
|
||
const fileName = filePath.split(/[\\/]/).pop() || "lob.txt";
|
||
const prepared = await post<{ downloadId: string }>("/api/query/large-value/download", { request, fileName });
|
||
signal?.throwIfAborted();
|
||
const anchor = document.createElement("a");
|
||
anchor.href = apiUrl(`/api/query/large-value/download/${encodeURIComponent(prepared.downloadId)}`);
|
||
anchor.download = fileName;
|
||
anchor.click();
|
||
}
|
||
|
||
export interface SnapshotExportRequest {
|
||
context: LargeValueRequest;
|
||
format: "csv" | "json";
|
||
columns: string[];
|
||
rows: unknown[][];
|
||
cells: Array<{ rowIndex: number; columnIndex: number; valueRef: string }>;
|
||
quoteMode?: "all" | "necessary";
|
||
nullLiteral?: string | null;
|
||
}
|
||
|
||
export async function exportSnapshotResult(request: SnapshotExportRequest, filePath: string, signal?: AbortSignal): Promise<void> {
|
||
signal?.throwIfAborted();
|
||
const fileName = filePath.split(/[\\/]/).pop() || `result.${request.format}`;
|
||
const prepared = await post<{ downloadId: string }>("/api/query/large-value/export", { request, fileName });
|
||
signal?.throwIfAborted();
|
||
const anchor = document.createElement("a");
|
||
anchor.href = apiUrl(`/api/query/large-value/download/${encodeURIComponent(prepared.downloadId)}`);
|
||
anchor.download = fileName;
|
||
anchor.click();
|
||
}
|
||
|
||
export async function closeQuerySession(connectionId: string, database: string, sessionId: string, clientSessionId?: string, catalog?: string, txnSessionId?: string): Promise<boolean> {
|
||
if (txnSessionId) throw new Error("Manual transaction management is only available in the desktop app.");
|
||
return post("/api/query/close-session", {
|
||
connectionId,
|
||
database,
|
||
sessionId,
|
||
clientSessionId,
|
||
catalog,
|
||
});
|
||
}
|
||
|
||
export async function closeClientConnectionSession(connectionId: string, database: string, clientSessionId: string, catalog?: string): Promise<boolean> {
|
||
return post("/api/query/close-client-session", {
|
||
connectionId,
|
||
database,
|
||
clientSessionId,
|
||
catalog,
|
||
});
|
||
}
|
||
|
||
export async function executeBatch(connectionId: string, database: string, statements: string[], schema?: string, timeoutSecs?: number, useTransaction?: boolean, boundStatements?: BlobBoundStatement[]): Promise<QueryResult> {
|
||
return post("/api/query/execute-batch", {
|
||
connectionId,
|
||
database,
|
||
statements,
|
||
schema,
|
||
timeoutSecs,
|
||
useTransaction,
|
||
boundStatements,
|
||
});
|
||
}
|
||
|
||
export async function executeScript(connectionId: string, database: string, sql: string, schema?: string): Promise<QueryResult> {
|
||
return post("/api/query/execute-script", {
|
||
connectionId,
|
||
database,
|
||
sql,
|
||
schema,
|
||
});
|
||
}
|
||
|
||
export async function executeScriptWith2pc(connectionId: string, database: string, statements: string[], schema?: string, destructiveConfirmed = false): Promise<any> {
|
||
return post("/api/query/execute-script-2pc", {
|
||
connectionId,
|
||
database,
|
||
statements,
|
||
schema,
|
||
destructiveConfirmed,
|
||
});
|
||
}
|
||
|
||
export async function executeInTransaction(connectionId: string, database: string, statements: string[], schema?: string, catalog?: string, boundStatements?: BlobBoundStatement[]): Promise<QueryResult> {
|
||
return post("/api/query/execute-in-transaction", {
|
||
connectionId,
|
||
database,
|
||
statements,
|
||
schema,
|
||
catalog,
|
||
boundStatements,
|
||
});
|
||
}
|
||
|
||
export async function beginManualTransaction(_connectionId: string, _database: string, _schema?: string, _catalog?: string): Promise<string> {
|
||
throw new Error("Manual transaction management is only available in the desktop app.");
|
||
}
|
||
|
||
export async function executeInManualTransaction(
|
||
_txnSessionId: string,
|
||
_sql: string,
|
||
_database: string,
|
||
_schema?: string,
|
||
_maxRows?: number,
|
||
_tableDataPreview?: boolean,
|
||
_pageSize?: number,
|
||
_resultSessionId?: string,
|
||
_classificationSql?: string,
|
||
_executionId?: string,
|
||
_timeoutSecs?: number,
|
||
_boundStatements?: BlobBoundStatement[],
|
||
_deferLobs?: boolean,
|
||
): Promise<QueryResult[]> {
|
||
throw new Error("Manual transaction management is only available in the desktop app.");
|
||
}
|
||
|
||
export async function commitManualTransaction(_txnSessionId: string): Promise<QueryResult> {
|
||
throw new Error("Manual transaction management is only available in the desktop app.");
|
||
}
|
||
|
||
export async function rollbackManualTransaction(_txnSessionId: string): Promise<QueryResult> {
|
||
throw new Error("Manual transaction management is only available in the desktop app.");
|
||
}
|
||
|
||
export async function cancelQueryAndWait(_executionId: string): Promise<{ requested: boolean; terminal: boolean }> {
|
||
throw new Error("Manual transaction cancellation confirmation is only available in the desktop app.");
|
||
}
|
||
|
||
export async function cancelQuery(executionId: string): Promise<boolean> {
|
||
const result = await post<boolean | { cancelled?: boolean }>("/api/query/cancel", { executionId });
|
||
return typeof result === "boolean" ? result : result.cancelled === true;
|
||
}
|
||
|
||
export interface ConditionalUpdateCancellationResult {
|
||
requested: boolean;
|
||
terminal: boolean;
|
||
databaseTerminal?: boolean;
|
||
}
|
||
|
||
export async function cancelConditionalUpdate(executionId: string, options?: { cancel?: boolean; typeRecovery?: import("@/types/database").TypeExecutionBinding }): Promise<ConditionalUpdateCancellationResult> {
|
||
return post("/api/query/cancel-conditional-update", { executionId, ...options });
|
||
}
|
||
|
||
export async function analyzeSqlReferences(sql: string, dialect?: string): Promise<SqlReferenceAnalysis> {
|
||
return post("/api/query/analyze-sql-references", { sql, dialect });
|
||
}
|
||
|
||
export async function findStatementAtCursor(sql: string, cursorPos: number, databaseType?: DatabaseType): Promise<string> {
|
||
return post("/api/query/find-statement-at-cursor", {
|
||
sql,
|
||
cursorPos,
|
||
databaseType,
|
||
});
|
||
}
|
||
|
||
export async function prepareQueryPaginationExecutionPlan(options: QueryPaginationExecutionPlanOptions): Promise<QueryPaginationExecutionPlan> {
|
||
return post("/api/query/prepare-pagination-plan", { options });
|
||
}
|
||
|
||
export async function buildSortedQuerySql(options: SortedQuerySqlOptions): Promise<QuerySqlBuildResult> {
|
||
return post("/api/query/build-sorted-sql", { options });
|
||
}
|
||
|
||
export async function buildExplainSql(options: BuildExplainSqlOptions): Promise<ExplainSqlBuildResult> {
|
||
return post("/api/query/build-explain-sql", { options });
|
||
}
|
||
|
||
export async function buildCreateUserSql(username: string, password: string, tablespace: string): Promise<string> {
|
||
return post("/api/query/build-create-user-sql", {
|
||
username,
|
||
password,
|
||
tablespace,
|
||
});
|
||
}
|
||
|
||
export async function getExplainInfo(connectionId: string, database: string | undefined, schema: string | undefined, sql: string, mode: string, executionId?: string, timeoutSecs?: number): Promise<string | undefined> {
|
||
// Match the Tauri path: transport and Agent failures must remain distinguishable from an empty plan.
|
||
return post<string>("/api/query/get-explain-info", {
|
||
connectionId,
|
||
database,
|
||
schema,
|
||
sql,
|
||
mode,
|
||
...(executionId ? { executionId } : {}),
|
||
...(timeoutSecs === undefined ? {} : { timeoutSecs }),
|
||
});
|
||
}
|
||
|
||
/** Plugin Host API: what the host and this connection can plan. Never connects. */
|
||
export async function getPluginPlanCapabilities(connectionId: string): Promise<PluginPlanCapabilities> {
|
||
return post<PluginPlanCapabilities>("/api/query/plugin-plan-capabilities", { connectionId });
|
||
}
|
||
|
||
/**
|
||
* Plugin Host API: acquires the estimated plan for caller-supplied SQL. The
|
||
* backend generates and owns the EXPLAIN statement; the request cannot carry one.
|
||
*/
|
||
export async function getPluginEstimatedPlan(request: PluginPlanRequest): Promise<PluginPlanResult> {
|
||
return post<PluginPlanResult>("/api/query/plugin-estimated-plan", request);
|
||
}
|
||
|
||
/**
|
||
* Plugin Host API (`host.data:read`): one read-only statement on a connection
|
||
* the user granted to `pluginId`. The backend enforces permission, grant, and gate.
|
||
*/
|
||
export async function queryPluginData(pluginId: string, request: PluginDataQueryRequest): Promise<PluginDataQueryResult> {
|
||
return post<PluginDataQueryResult>("/api/plugin/data/query", { pluginId, request });
|
||
}
|
||
|
||
export async function getPluginDataGrants(pluginId: string): Promise<PluginDataGrant[]> {
|
||
return post<PluginDataGrant[]>("/api/plugin/data/grants", { pluginId });
|
||
}
|
||
|
||
export async function setPluginDataGrant(pluginId: string, connectionId: string, granted: boolean): Promise<PluginDataGrant[]> {
|
||
return post<PluginDataGrant[]>("/api/plugin/data/grant", { pluginId, connectionId, granted });
|
||
}
|
||
|
||
export async function buildDroppedFilePreviewSql(options: DroppedFilePreviewSqlOptions): Promise<string | undefined> {
|
||
const result = await post<string | null>("/api/query/build-dropped-file-preview-sql", { options });
|
||
return result ?? undefined;
|
||
}
|
||
|
||
export async function buildTableSelectSql(options: BuildTableSelectSqlOptions): Promise<string> {
|
||
return post("/api/query/build-table-select-sql", { options, includeDatabaseName: options.includeDatabaseName === true });
|
||
}
|
||
|
||
export async function buildDatabaseSearchSql(options: DatabaseSearchSqlOptions): Promise<DatabaseSearchSql | null> {
|
||
return post("/api/query/build-database-search-sql", { options });
|
||
}
|
||
|
||
export async function buildDatabaseSearchColumnsSql(options: import("@/lib/database/databaseSearchColumns").DatabaseSearchColumnsSqlOptions): Promise<{ sql: string } | null> {
|
||
return post("/api/query/build-database-search-columns-sql", { options });
|
||
}
|
||
|
||
export async function buildDatabaseSearchProbeSql(options: import("@/lib/database/databaseSearchProbe").DatabaseSearchProbeSqlOptions): Promise<{ sql: string; includedIndices: number[] } | null> {
|
||
return post("/api/query/build-database-search-probe-sql", { options });
|
||
}
|
||
|
||
export async function buildDatabaseStructureSearchSql(options: DatabaseStructureSearchSqlOptions): Promise<DatabaseStructureSearchSql | null> {
|
||
return post("/api/query/build-database-structure-search-sql", { options });
|
||
}
|
||
|
||
export async function supportsDatabaseStructureSearch(databaseType?: DatabaseType, driverProfile?: string): Promise<boolean> {
|
||
return post("/api/query/supports-database-structure-search", { databaseType, driverProfile });
|
||
}
|
||
|
||
export async function buildSearchResultWhere(options: SearchResultWhereOptions): Promise<string> {
|
||
return post("/api/query/build-search-result-where", { options });
|
||
}
|
||
|
||
export async function buildRenameObjectSql(options: BuildRenameObjectSqlOptions): Promise<string> {
|
||
return post("/api/query/build-rename-object-sql", { options });
|
||
}
|
||
|
||
export async function buildRenameDatabaseSql(options: { databaseType?: DatabaseType; oldName: string; newName: string; terminateConnections: boolean }): Promise<string> {
|
||
return post("/api/query/build-rename-database-sql", options);
|
||
}
|
||
|
||
export async function buildRenameDatabasePreflightSql(options: { databaseType?: DatabaseType; databaseName: string }): Promise<string> {
|
||
return post("/api/query/build-rename-database-preflight-sql", options);
|
||
}
|
||
|
||
export async function buildCreateDatabaseSql(options: CreateDatabaseSqlOptions): Promise<string> {
|
||
return post("/api/query/build-create-database-sql", { options });
|
||
}
|
||
|
||
export async function buildDuckDbAttachDatabaseSql(path: string, name: string): Promise<string> {
|
||
return post("/api/query/build-duckdb-attach-database-sql", {
|
||
options: { path, name },
|
||
});
|
||
}
|
||
|
||
export async function buildSqliteAttachDatabaseSql(path: string, name: string): Promise<string> {
|
||
return post("/api/query/build-sqlite-attach-database-sql", {
|
||
options: { path, name },
|
||
});
|
||
}
|
||
|
||
export async function buildDropObjectSql(options: DropObjectSqlOptions): Promise<string> {
|
||
return post("/api/query/build-drop-object-sql", { options });
|
||
}
|
||
|
||
export async function buildDropTableSql(options: TableAdminSqlOptions): Promise<string> {
|
||
return post("/api/query/build-drop-table-sql", { options });
|
||
}
|
||
|
||
export async function buildDropTableChildObjectSql(options: DropTableChildObjectSqlOptions): Promise<string> {
|
||
return post("/api/query/build-drop-table-child-object-sql", { options });
|
||
}
|
||
|
||
export async function buildEmptyTableSql(options: TableAdminSqlOptions): Promise<string> {
|
||
return post("/api/query/build-empty-table-sql", { options });
|
||
}
|
||
|
||
export async function buildTruncateTableSql(options: TableAdminSqlOptions): Promise<string> {
|
||
return post("/api/query/build-truncate-table-sql", { options });
|
||
}
|
||
|
||
export async function buildVacuumTableSql(options: VacuumTableSqlOptions): Promise<string> {
|
||
return post("/api/query/build-vacuum-table-sql", { options });
|
||
}
|
||
|
||
export async function buildMysqlAutoIncrementSql(options: MysqlAutoIncrementSqlOptions): Promise<string> {
|
||
return post("/api/query/build-mysql-auto-increment-sql", { options });
|
||
}
|
||
|
||
export async function buildDropDatabaseSql(options: DatabaseNameSqlOptions): Promise<string> {
|
||
return post("/api/query/build-drop-database-sql", { options });
|
||
}
|
||
|
||
export async function buildCreateSchemaSql(options: SchemaNameSqlOptions): Promise<string> {
|
||
return post("/api/query/build-create-schema-sql", { options });
|
||
}
|
||
|
||
export async function buildUpdateDatabasePropertiesSql(options: DatabasePropertyEditSqlOptions): Promise<string> {
|
||
return post("/api/query/build-update-database-properties-sql", { options });
|
||
}
|
||
|
||
export async function buildDropSchemaSql(options: SchemaNameSqlOptions): Promise<string> {
|
||
return post("/api/query/build-drop-schema-sql", { options });
|
||
}
|
||
|
||
export async function buildDuplicateTableStructureSql(options: DuplicateTableStructureSqlOptions): Promise<string> {
|
||
return post("/api/query/build-duplicate-table-structure-sql", { options });
|
||
}
|
||
|
||
export async function buildCopyTableDataSql(options: CopyTableDataSqlOptions): Promise<string> {
|
||
return post("/api/query/build-copy-table-data-sql", { options });
|
||
}
|
||
|
||
export async function buildExecutableObjectSourceStatements(input: BuildEditableObjectSourceSqlInput): Promise<string[]> {
|
||
return post("/api/query/build-executable-object-source-statements", {
|
||
input,
|
||
});
|
||
}
|
||
|
||
export async function validateOracleExecutableObjectSource(input: ValidateOracleExecutableObjectSourceInput): Promise<void> {
|
||
return post("/api/query/validate-oracle-executable-object-source", { input });
|
||
}
|
||
|
||
export async function fingerprintOracleRoutineObjectSource(input: FingerprintOracleRoutineObjectSourceInput): Promise<string> {
|
||
return post("/api/query/fingerprint-oracle-routine-object-source", { input });
|
||
}
|
||
|
||
export async function buildExecutableObjectSourceSql(input: BuildEditableObjectSourceSqlInput): Promise<string> {
|
||
return post("/api/query/build-executable-object-source-sql", { input });
|
||
}
|
||
|
||
export async function buildEditableObjectSource(input: BuildEditableObjectSourceSqlInput): Promise<string> {
|
||
return post("/api/query/build-editable-object-source", { input });
|
||
}
|
||
|
||
export async function buildRoutineRenameObjectSourceStatements(input: BuildRoutineRenameObjectSourceInput): Promise<string[]> {
|
||
return post("/api/query/build-routine-rename-object-source-statements", {
|
||
input,
|
||
});
|
||
}
|
||
|
||
export async function rewriteOracleRoutineReferences(input: RewriteOracleRoutineReferencesInput): Promise<string> {
|
||
return post("/api/query/rewrite-oracle-routine-references", { input });
|
||
}
|
||
|
||
export async function rewriteOracleTableReferences(input: OracleTableReferenceRewriteInput): Promise<string> {
|
||
return post("/api/query/rewrite-oracle-table-references", { input });
|
||
}
|
||
|
||
export async function buildViewDdlSql(input: BuildViewDdlInput): Promise<string> {
|
||
return post("/api/query/build-view-ddl-sql", { input });
|
||
}
|
||
|
||
export async function parseOracleObjectName(input: string): Promise<string> {
|
||
return post("/api/query/parse-oracle-object-name", { input });
|
||
}
|
||
|
||
export async function buildOracleViewRenameSource(input: OracleViewRenameSourceInput): Promise<OracleViewRenameSource> {
|
||
return post("/api/query/build-oracle-view-rename-source", { input });
|
||
}
|
||
|
||
export async function reviewOracleViewRenameSource(input: OracleViewRenameSourceInput): Promise<OracleViewRenameSourceReview> {
|
||
return post("/api/query/review-oracle-view-rename-source", { input });
|
||
}
|
||
|
||
export async function buildTableStructureChangeSql(options: BuildTableStructureChangeSqlOptions, starrocks?: StarRocksAlterOptions, mysqlTableCharset?: MysqlTableCharsetChange): Promise<TableStructureChangeSql> {
|
||
return post("/api/query/build-table-structure-change-sql", { options, starrocks, mysqlTableCharset });
|
||
}
|
||
|
||
export async function buildTableOwnerChangeSql(options: BuildTableOwnerChangeSqlOptions): Promise<TableStructureChangeSql> {
|
||
return post("/api/query/build-table-owner-change-sql", { options });
|
||
}
|
||
|
||
export async function buildTablePartitionOperationSql(options: TablePartitionSqlOptions): Promise<TableStructureChangeSql> {
|
||
return post("/api/query/build-table-partition-operation-sql", { options });
|
||
}
|
||
|
||
export async function buildCreatePartitionedTableSql(options: BuildCreatePartitionedTableSqlOptions): Promise<TableStructureChangeSql> {
|
||
return post("/api/query/build-create-partitioned-table-sql", { options: options.options, partitioning: options.partitioning });
|
||
}
|
||
|
||
export async function previewSqliteTableStructureChange(connectionId: string, database: string, options: BuildTableStructureChangeSqlOptions): Promise<SqliteTableStructureChangePreview> {
|
||
return post("/api/query/preview-sqlite-table-structure-change", {
|
||
connectionId,
|
||
database,
|
||
options,
|
||
});
|
||
}
|
||
|
||
export async function applySqliteTableStructureChange(connectionId: string, database: string, options: BuildTableStructureChangeSqlOptions, schemaRevision: string): Promise<QueryResult> {
|
||
return post("/api/query/apply-sqlite-table-structure-change", {
|
||
connectionId,
|
||
database,
|
||
options,
|
||
schemaRevision,
|
||
});
|
||
}
|
||
|
||
export async function buildCreateTableSql(options: BuildTableStructureChangeSqlOptions, serverVersion?: string, dialectOptions?: CreateTableDialectOptions): Promise<TableStructureChangeSql> {
|
||
return post("/api/query/build-create-table-sql", { options, serverVersion, dialectOptions });
|
||
}
|
||
|
||
export async function buildSingleColumnAlterSql(options: BuildSingleColumnAlterSqlOptions): Promise<TableStructureChangeSql> {
|
||
return post("/api/query/build-single-column-alter-sql", { options });
|
||
}
|
||
|
||
export async function analyzeEditableQueryEditability(sql: string): Promise<QueryEditability> {
|
||
return post("/api/query/analyze-editability", { sql });
|
||
}
|
||
|
||
export async function prepareDataGridSave(options: DataGridSaveStatementOptions, driverProfile?: string): Promise<DataGridSavePreparation> {
|
||
return post("/api/query/prepare-data-grid-save", { options, driverProfile });
|
||
}
|
||
|
||
export async function extractDataGridSelection(request: DataGridExtractRequest): Promise<DataGridExtractResult> {
|
||
return post("/api/query/extract-data-grid-selection", { request });
|
||
}
|
||
|
||
export async function buildDataGridCopyUpdateStatements(options: DataGridCopyUpdateStatementOptions): Promise<string[]> {
|
||
return post("/api/query/build-data-grid-copy-update-statements", { options });
|
||
}
|
||
|
||
export async function buildDataGridCopyInsertStatement(options: DataGridCopyInsertStatementOptions): Promise<string | undefined> {
|
||
const result = await post<string | null>("/api/query/build-data-grid-copy-insert-statement", { options });
|
||
return result ?? undefined;
|
||
}
|
||
|
||
export async function buildDmlChangePreviewSql(options: DmlChangePreviewSqlOptions): Promise<DmlChangePreviewSqlResult> {
|
||
return post("/api/query/build-dml-change-preview-sql", { options });
|
||
}
|
||
|
||
export async function buildDataGridContextFilterCondition(options: DataGridContextFilterConditionOptions): Promise<string | undefined> {
|
||
const result = await post<string | null>("/api/query/build-data-grid-context-filter-condition", { options });
|
||
return result ?? undefined;
|
||
}
|
||
|
||
export async function buildDataGridColumnValueFilterCondition(options: DataGridColumnValueFilterConditionOptions): Promise<string | undefined> {
|
||
const result = await post<string | null>("/api/query/build-data-grid-column-value-filter-condition", { options });
|
||
return result ?? undefined;
|
||
}
|
||
|
||
export async function buildDataGridColumnValuesFilterCondition(options: DataGridColumnValuesFilterConditionOptions): Promise<string | undefined> {
|
||
const result = await post<string | null>("/api/query/build-data-grid-column-values-filter-condition", { options });
|
||
return result ?? undefined;
|
||
}
|
||
|
||
export async function buildDataGridColumnDistinctValuesSql(options: DataGridColumnDistinctValuesSqlOptions): Promise<string> {
|
||
return post("/api/query/build-data-grid-column-distinct-values-sql", {
|
||
options,
|
||
});
|
||
}
|
||
|
||
export async function buildDataGridCountSql(options: DataGridCountSqlOptions): Promise<string> {
|
||
return post("/api/query/build-data-grid-count-sql", { options });
|
||
}
|
||
|
||
export async function buildDataGridConditionalUpdateSql(options: DataGridConditionalUpdateSqlOptions): Promise<string | undefined> {
|
||
const result = await post<string | null>("/api/query/build-data-grid-conditional-update-sql", { options });
|
||
return result ?? undefined;
|
||
}
|
||
|
||
export async function buildHiveTablePropertiesSql(options: HiveTablePropertiesSqlOptions): Promise<string> {
|
||
return post("/api/query/build-hive-table-properties-sql", { options });
|
||
}
|
||
|
||
export async function buildExportInsertStatements(options: BuildExportInsertStatementsOptions): Promise<string[]> {
|
||
return post("/api/query/build-export-insert-statements", { options });
|
||
}
|
||
|
||
export async function buildExportSqlInsert(options: BuildExportSqlInsertOptions): Promise<string> {
|
||
return post("/api/query/build-export-sql-insert", { options });
|
||
}
|
||
|
||
export async function buildDatabaseSqlExport(options: BuildDatabaseSqlExportOptions): Promise<string> {
|
||
return post("/api/query/build-database-sql-export", { options });
|
||
}
|
||
|
||
export async function prepareDataCompare(options: DataComparePreparationOptions): Promise<DataComparePreparation> {
|
||
return post("/api/data-compare/prepare", options);
|
||
}
|
||
|
||
export async function prepareDataCompareFromTables(options: DataCompareFromTablesOptions): Promise<DataCompareFromTablesPreparation> {
|
||
return post("/api/data-compare/prepare-from-tables", options);
|
||
}
|
||
|
||
export async function prepareDataCompareMissingTarget(options: import("@/lib/dataGrid/dataCompare").DataCompareMissingTargetOptions): Promise<DataCompareFromTablesPreparation> {
|
||
return post("/api/data-compare/prepare-missing-target", options);
|
||
}
|
||
|
||
export async function buildDataCompareSyncPlan(options: DataCompareSyncPlanOptions): Promise<DataCompareSyncPlan> {
|
||
return post("/api/data-compare/build-sync-plan", options);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// AI
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function aiComplete(request: AiCompletionRequest): Promise<string> {
|
||
return post("/api/ai/complete", { request });
|
||
}
|
||
|
||
export async function aiStream(sessionId: string, request: AiCompletionRequest, onChunk: (chunk: AiStreamChunk) => void): Promise<void> {
|
||
const res = await fetch(apiUrl("/api/ai/stream"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ session_id: sessionId, request }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
|
||
const reader = res.body!.getReader();
|
||
const decoder = new TextDecoder();
|
||
let buffer = "";
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read();
|
||
if (done) break;
|
||
buffer += decoder.decode(value, { stream: true });
|
||
|
||
const lines = buffer.split("\n");
|
||
buffer = lines.pop() || "";
|
||
|
||
for (const line of lines) {
|
||
if (line.startsWith("data:")) {
|
||
const data = line.slice(5).trim();
|
||
if (data && data !== "[DONE]") {
|
||
let chunk: AiStreamChunk;
|
||
try {
|
||
chunk = JSON.parse(data);
|
||
} catch {
|
||
// skip malformed JSON
|
||
continue;
|
||
}
|
||
if (chunk.error) throw new Error(chunk.error);
|
||
onChunk(chunk);
|
||
if (chunk.done) return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
export async function aiCancelStream(sessionId: string): Promise<boolean> {
|
||
return post("/api/ai/cancel-stream", { sessionId });
|
||
}
|
||
|
||
/** Answers a pending plugin tool approval of the agent run `sessionId`; false when nothing was waiting. */
|
||
export async function resolveAiToolApproval(sessionId: string, approvalId: string, approved: boolean): Promise<boolean> {
|
||
return post("/api/ai/tool-approval", { sessionId, approvalId, approved });
|
||
}
|
||
|
||
/** Plugin ids whose MCP tools the built-in AI agent may call. */
|
||
export async function getAiPluginToolPlugins(): Promise<string[]> {
|
||
return get("/api/ai/plugin-tools/plugins");
|
||
}
|
||
|
||
export async function setAiPluginToolPluginEnabled(pluginId: string, enabled: boolean): Promise<string[]> {
|
||
return post("/api/ai/plugin-tools/plugins", { pluginId, enabled });
|
||
}
|
||
|
||
/** Tools the built-in AI would get from a plugin (may start its sidecar). */
|
||
export async function previewPluginAiTools(pluginId: string): Promise<PluginToolPreview> {
|
||
return post("/api/ai/plugin-tools/preview", { pluginId });
|
||
}
|
||
|
||
export async function aiTestConnection(config: AiConfig): Promise<AiTestConnectionResult> {
|
||
return post("/api/ai/test-connection", { config });
|
||
}
|
||
|
||
export async function aiListModels(config: AiConfig): Promise<AiModelInfo[]> {
|
||
return post("/api/ai/models", { config });
|
||
}
|
||
|
||
export async function aiResolveModelEffort(config: AiConfig, modelId: string): Promise<AiEffortCapability> {
|
||
return post("/api/ai/model-effort", { config, modelId });
|
||
}
|
||
|
||
export async function saveAiChatSelection(selection: AiChatSelectionState): Promise<void> {
|
||
return post("/api/ai/chat-selection", { selection });
|
||
}
|
||
|
||
export async function loadAiChatSelection(): Promise<AiChatSelectionState | null> {
|
||
return get("/api/ai/chat-selection");
|
||
}
|
||
|
||
export type { AgentEvent } from "@/lib/backend/tauri";
|
||
|
||
function isAgentEvent(v: unknown): v is import("@/lib/backend/tauri").AgentEvent {
|
||
return typeof v === "object" && v !== null && "type" in v && typeof (v as Record<string, unknown>).type === "string";
|
||
}
|
||
|
||
export async function aiAgentStream(
|
||
sessionId: string,
|
||
request: AiCompletionRequest,
|
||
connectionId: string,
|
||
database: string,
|
||
schema: string | undefined,
|
||
dbType: string,
|
||
onEvent: (event: import("@/lib/backend/tauri").AgentEvent) => void,
|
||
mode?: string,
|
||
allowWriteSql = false,
|
||
confirmedWriteSql?: string,
|
||
confirmedConnectionId?: string,
|
||
confirmedDatabase?: string,
|
||
confirmedSchema?: string,
|
||
signal?: AbortSignal,
|
||
selectedDatabases?: string[],
|
||
allowSkills = false,
|
||
): Promise<string> {
|
||
const res = await fetch(apiUrl("/api/ai/agent-stream"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
sessionId,
|
||
request,
|
||
connectionId,
|
||
database,
|
||
schema,
|
||
dbType,
|
||
mode: mode || "ask",
|
||
allowWriteSql,
|
||
confirmedWriteSql,
|
||
confirmedConnectionId,
|
||
confirmedDatabase,
|
||
confirmedSchema,
|
||
selectedDatabases,
|
||
// The web server ignores this by design: local skill files are never
|
||
// exposed to it (there is no request field on that route either).
|
||
allowSkills,
|
||
}),
|
||
signal,
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
|
||
const reader = res.body!.getReader();
|
||
const decoder = new TextDecoder();
|
||
let buffer = "";
|
||
let result = "";
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read();
|
||
if (done) break;
|
||
buffer += decoder.decode(value, { stream: true });
|
||
|
||
const lines = buffer.split("\n");
|
||
buffer = lines.pop() || "";
|
||
|
||
for (const line of lines) {
|
||
if (line.startsWith("data:")) {
|
||
const data = line.slice(5).trim();
|
||
if (data && data !== "[DONE]") {
|
||
let parsed: unknown;
|
||
try {
|
||
parsed = JSON.parse(data);
|
||
} catch {
|
||
// skip malformed JSON
|
||
continue;
|
||
}
|
||
if (!isAgentEvent(parsed)) {
|
||
console.warn("[aiAgentStream] Skipping invalid agent event:", data);
|
||
continue;
|
||
}
|
||
onEvent(parsed);
|
||
if (parsed.type === "error") {
|
||
throw new Error(parsed.message);
|
||
}
|
||
if (parsed.type === "agent_end") {
|
||
result = data;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
export async function saveAiConfig(config: AiConfig): Promise<void> {
|
||
return post("/api/ai/config", { config });
|
||
}
|
||
|
||
export async function saveAiProviderConfig(provider: string, config: AiConfig): Promise<void> {
|
||
return post("/api/ai/provider-config", { provider, config });
|
||
}
|
||
|
||
export async function loadAiProviderConfigs(): Promise<Record<string, AiConfig>> {
|
||
return get("/api/ai/provider-configs");
|
||
}
|
||
|
||
export async function loadAiConfig(): Promise<AiConfig | null> {
|
||
return get("/api/ai/config");
|
||
}
|
||
|
||
export async function saveAiConfigs(configs: import("@/types/ai").AiConfigItem[]): Promise<void> {
|
||
return post("/api/ai/configs", { configs });
|
||
}
|
||
|
||
export async function loadAiConfigs(): Promise<import("@/types/ai").AiConfigItem[]> {
|
||
return get("/api/ai/configs");
|
||
}
|
||
|
||
export async function setDefaultAiConfig(configId: string): Promise<void> {
|
||
return post("/api/ai/default-config", { configId });
|
||
}
|
||
|
||
export async function saveAiConfigItem(config: import("@/types/ai").AiConfigItem): Promise<void> {
|
||
return post("/api/ai/config-item", { config });
|
||
}
|
||
|
||
export async function deleteAiConfig(configId: string): Promise<void> {
|
||
return del(`/api/ai/config/${configId}`);
|
||
}
|
||
|
||
export async function loadCcSwitchAiConfigs(): Promise<import("@/types/ai").CcSwitchImportResult> {
|
||
throw new Error("ccSwitchImportDesktopOnly");
|
||
}
|
||
|
||
export async function loadMagpieAiConfigs(): Promise<import("@/types/ai").CcSwitchImportResult> {
|
||
throw new Error("magpieImportDesktopOnly");
|
||
}
|
||
|
||
export async function loadDesktopSettings(): Promise<DesktopSettings> {
|
||
try {
|
||
const raw = safeLocalStorageGet(DESKTOP_SETTINGS_STORAGE_KEY);
|
||
return raw
|
||
? {
|
||
...DEFAULT_DESKTOP_SETTINGS,
|
||
...(JSON.parse(raw) as Partial<DesktopSettings>),
|
||
}
|
||
: { ...DEFAULT_DESKTOP_SETTINGS };
|
||
} catch {
|
||
return { ...DEFAULT_DESKTOP_SETTINGS };
|
||
}
|
||
}
|
||
|
||
export async function saveDesktopSettings(settings: DesktopSettings): Promise<void> {
|
||
safeLocalStorageSet(DESKTOP_SETTINGS_STORAGE_KEY, JSON.stringify({ ...DEFAULT_DESKTOP_SETTINGS, ...settings }));
|
||
}
|
||
|
||
export async function loadMcpGlobalPolicy(): Promise<McpGlobalPolicy> {
|
||
return get("/api/app-settings/mcp-policy");
|
||
}
|
||
|
||
export async function saveMcpGlobalPolicy(policy: Omit<McpGlobalPolicy, "configured">): Promise<void> {
|
||
const res = await fetch(apiUrl("/api/app-settings/mcp-policy"), {
|
||
method: "PUT",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify(policy),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
}
|
||
|
||
export async function loadMcpHttpServerSettings(): Promise<import("@/lib/backend/tauri").McpHttpServerSettings> {
|
||
return { enabled: false, host: "127.0.0.1", port: 5225, path: "/mcp", allowRemote: false, allowedHosts: [], allowedOrigins: [] };
|
||
}
|
||
|
||
export async function saveMcpHttpServerSettings(_settings: import("@/lib/backend/tauri").McpHttpServerSettings): Promise<import("@/lib/backend/tauri").McpHttpServerStatus> {
|
||
throw new Error("The MCP HTTP server is available only in DBX Desktop");
|
||
}
|
||
|
||
export async function mcpHttpServerStatus(): Promise<import("@/lib/backend/tauri").McpHttpServerStatus> {
|
||
return { enabled: false, running: false, endpoint: null, accessToken: null, lastError: null, recentLogs: [] };
|
||
}
|
||
|
||
export async function rotateMcpHttpServerToken(): Promise<import("@/lib/backend/tauri").McpHttpServerStatus> {
|
||
throw new Error("The MCP HTTP server is available only in DBX Desktop");
|
||
}
|
||
|
||
export async function loadWebMcpHttpStatus(): Promise<import("@/lib/backend/tauri").WebMcpHttpStatus> {
|
||
return get("/api/app-settings/mcp-http-status");
|
||
}
|
||
|
||
export async function saveWebMcpHttpSettings(settings: import("@/lib/backend/tauri").WebMcpHttpSettings): Promise<import("@/lib/backend/tauri").WebMcpHttpStatus> {
|
||
const res = await fetch(apiUrl("/api/app-settings/mcp-http"), {
|
||
method: "PUT",
|
||
headers: { "Content-Type": "application/json", "X-DBX-MCP-Settings": "1" },
|
||
body: JSON.stringify(settings),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
export async function rotateWebMcpToken(): Promise<import("@/lib/backend/tauri").WebMcpHttpStatus> {
|
||
const res = await fetch(apiUrl("/api/app-settings/mcp-http/rotate-token"), { method: "POST", headers: { "X-DBX-MCP-Settings": "1" } });
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
export async function loadWebCapabilities(): Promise<import("@/lib/backend/tauri").WebCapabilities> {
|
||
return get("/api/app-settings/web-capabilities");
|
||
}
|
||
|
||
export async function loadMaxAgentTurns(): Promise<number> {
|
||
return get("/api/app-settings/max-agent-turns");
|
||
}
|
||
|
||
export async function loadSqlFileUploadMaxBytes(): Promise<number> {
|
||
return get("/api/app-settings/sql-file-upload-max-bytes");
|
||
}
|
||
|
||
export async function saveSqlFileUploadMaxMb(sqlFileUploadMaxMb: number): Promise<void> {
|
||
const res = await fetch(apiUrl("/api/app-settings/sql-file-upload-max-bytes"), {
|
||
method: "PUT",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ sqlFileUploadMaxMb }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
}
|
||
|
||
export async function saveMaxAgentTurns(maxAgentTurns: number): Promise<void> {
|
||
const res = await fetch(apiUrl("/api/app-settings/max-agent-turns"), {
|
||
method: "PUT",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ maxAgentTurns }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
}
|
||
|
||
export async function loadHistoryRetentionLimit(): Promise<number> {
|
||
return get("/api/app-settings/history-retention-limit");
|
||
}
|
||
|
||
export async function saveHistoryRetentionLimit(limit: number): Promise<void> {
|
||
const res = await fetch(apiUrl("/api/app-settings/history-retention-limit"), {
|
||
method: "PUT",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ limit }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
}
|
||
|
||
export async function loadMcpHistoryRetentionLimit(): Promise<number> {
|
||
return get("/api/app-settings/mcp-history-retention-limit");
|
||
}
|
||
|
||
export async function saveMcpHistoryRetentionLimit(limit: number): Promise<void> {
|
||
const res = await fetch(apiUrl("/api/app-settings/mcp-history-retention-limit"), {
|
||
method: "PUT",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ limit }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
}
|
||
|
||
export async function loadMaxRetries(): Promise<number> {
|
||
return get("/api/app-settings/max-retries");
|
||
}
|
||
|
||
export async function saveMaxRetries(maxRetries: number): Promise<void> {
|
||
const res = await fetch(apiUrl("/api/app-settings/max-retries"), {
|
||
method: "PUT",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ maxRetries }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
}
|
||
|
||
export type { OpenTabsStatePayload, PersistedEditorGroup } from "@/lib/app/openTabsPersistence";
|
||
import type { OpenTabsStatePayload } from "@/lib/app/openTabsPersistence";
|
||
|
||
export async function loadEditorSettings(): Promise<unknown | null> {
|
||
return loadBrowserAppState("editor_settings");
|
||
}
|
||
|
||
export async function saveEditorSettings(settings: unknown): Promise<void> {
|
||
await saveBrowserAppState("editor_settings", settings);
|
||
}
|
||
|
||
// Background images are desktop-only: the web backend has no local data dir.
|
||
export interface BackgroundImageInfo {
|
||
storedPath: string;
|
||
fileName: string;
|
||
}
|
||
|
||
export async function saveBackgroundImage(_sourcePath: string): Promise<BackgroundImageInfo> {
|
||
throw new Error("Background images are only supported in the desktop app");
|
||
}
|
||
|
||
export async function clearBackgroundImage(_storedPath: string): Promise<void> {
|
||
throw new Error("Background images are only supported in the desktop app");
|
||
}
|
||
|
||
export async function readBackgroundImage(_storedPath: string): Promise<string> {
|
||
throw new Error("Background images are only supported in the desktop app");
|
||
}
|
||
|
||
export async function checkBackgroundImage(_storedPath: string): Promise<boolean> {
|
||
throw new Error("Background images are only supported in the desktop app");
|
||
}
|
||
|
||
export async function loadOpenTabsState(): Promise<OpenTabsStatePayload | null> {
|
||
const value = await loadBrowserAppState("open_tabs");
|
||
if (!value || typeof value !== "object") return null;
|
||
const payload = value as Partial<OpenTabsStatePayload>;
|
||
return Array.isArray(payload.tabs)
|
||
? {
|
||
tabs: payload.tabs,
|
||
activeTabId: typeof payload.activeTabId === "string" ? payload.activeTabId : null,
|
||
...(payload.groups !== undefined ? { groups: payload.groups } : {}),
|
||
...(typeof payload.focusedGroupId === "string" ? { focusedGroupId: payload.focusedGroupId } : {}),
|
||
...(payload.orientation === "vertical" || payload.orientation === "horizontal" ? { orientation: payload.orientation } : {}),
|
||
...(Array.isArray(payload.sizes) ? { sizes: payload.sizes } : {}),
|
||
}
|
||
: null;
|
||
}
|
||
|
||
export async function saveOpenTabsState(payload: OpenTabsStatePayload): Promise<void> {
|
||
await saveBrowserAppState("open_tabs", payload);
|
||
}
|
||
|
||
export async function saveDetachedTabHandoff(_tabId: string, _handoff: DetachedTabHandoff): Promise<void> {}
|
||
|
||
export async function loadDetachedTabHandoff(_tabId: string): Promise<DetachedTabHandoff | null> {
|
||
return null;
|
||
}
|
||
|
||
export async function listDetachedTabHandoffs(): Promise<DetachedTabHandoff[]> {
|
||
return [];
|
||
}
|
||
|
||
export async function deleteDetachedTabHandoff(_tabId: string): Promise<void> {}
|
||
|
||
export async function approveDetachedWindowClose(): Promise<void> {}
|
||
|
||
export async function loadSavedSqlEditorPositions(): Promise<unknown[] | null> {
|
||
const value = await loadBrowserAppState("saved_sql_editor_positions");
|
||
return Array.isArray(value) ? value : null;
|
||
}
|
||
|
||
export async function saveSavedSqlEditorPositions(positions: unknown[]): Promise<void> {
|
||
await saveBrowserAppState("saved_sql_editor_positions", positions);
|
||
}
|
||
|
||
export async function loadTransferTaskLibrary(): Promise<unknown | null> {
|
||
return loadBrowserAppState("transfer_task_library");
|
||
}
|
||
|
||
export async function saveTransferTaskLibrary(library: unknown): Promise<void> {
|
||
await saveBrowserAppState("transfer_task_library", library);
|
||
}
|
||
|
||
export async function completeAppClose(_action: "quit" | "hide"): Promise<void> {
|
||
return undefined;
|
||
}
|
||
|
||
export async function requestAppClose(): Promise<void> {
|
||
return undefined;
|
||
}
|
||
|
||
/** #11787:Web 形态没有宿主层可配合(浏览器自行处理 Ctrl+W/Ctrl+T 等保留键),空实现。 */
|
||
export async function setShortcutRecording(_recording: boolean): Promise<void> {
|
||
return undefined;
|
||
}
|
||
|
||
export interface DriverStoreMigrationResult {
|
||
driver_store_dir: string | null;
|
||
plugin_store_dir: string | null;
|
||
agent_store_dir: string | null;
|
||
plugins_dir: string;
|
||
agents_dir: string;
|
||
migrated_plugins: boolean;
|
||
migrated_agents: boolean;
|
||
}
|
||
|
||
export async function setDriverStoreDir(_newDir: string | null): Promise<DriverStoreMigrationResult> {
|
||
throw new Error("Not available in web mode");
|
||
}
|
||
|
||
export async function setPluginStoreDir(_newDir: string | null): Promise<DriverStoreMigrationResult> {
|
||
throw new Error("Not available in web mode");
|
||
}
|
||
|
||
export async function setAgentStoreDir(_newDir: string | null): Promise<DriverStoreMigrationResult> {
|
||
throw new Error("Not available in web mode");
|
||
}
|
||
|
||
export interface DriverStorePathInfo {
|
||
driver_store_dir: string | null;
|
||
plugin_store_dir: string | null;
|
||
agent_store_dir: string | null;
|
||
plugins_dir: string;
|
||
agents_dir: string;
|
||
}
|
||
|
||
export async function getDriverStorePath(): Promise<DriverStorePathInfo> {
|
||
throw new Error("Not available in web mode");
|
||
}
|
||
|
||
export interface WebDavConfig {
|
||
endpoint: string;
|
||
username?: string;
|
||
password?: string;
|
||
remotePath?: string;
|
||
userAgent?: string;
|
||
}
|
||
|
||
export interface WebDavSyncSummary {
|
||
remotePath: string;
|
||
bytes: number;
|
||
exportedAt?: string;
|
||
appVersion?: string;
|
||
}
|
||
|
||
export type { PluginUiStorageItemRef, SyncCatalogItem, SyncSelection, SyncSnapshotCatalog };
|
||
|
||
export interface WebDavDownloadResult {
|
||
summary: WebDavSyncSummary;
|
||
editorSettings?: unknown;
|
||
desktopSettings: DesktopSettings;
|
||
applySummary: {
|
||
encryptedSecretsPresent: boolean;
|
||
secretsApplied: boolean;
|
||
};
|
||
}
|
||
|
||
export interface WebDavPasswordStatus {
|
||
hasSavedPassword: boolean;
|
||
}
|
||
|
||
export interface WebDavSyncSecretsStatus {
|
||
enabled: boolean;
|
||
hasSavedPassphrase: boolean;
|
||
}
|
||
|
||
export type SnippetProvider = "github" | "gitee" | "gitlab";
|
||
|
||
export interface SnippetSyncConfig {
|
||
provider: SnippetProvider;
|
||
instanceUrl?: string;
|
||
token?: string;
|
||
snippetId?: string;
|
||
replaceLegacySnippet?: boolean;
|
||
}
|
||
|
||
export interface SnippetSyncSettings {
|
||
snippetId?: string;
|
||
legacyCleanupRequiredId?: string;
|
||
}
|
||
|
||
export interface SnippetSyncSummary {
|
||
provider: SnippetProvider;
|
||
snippetId: string;
|
||
bytes: number;
|
||
exportedAt?: string;
|
||
appVersion?: string;
|
||
remoteContentHash?: string;
|
||
unchanged: boolean;
|
||
legacyCleanupRequiredId?: string;
|
||
}
|
||
|
||
export interface SnippetDownloadResult {
|
||
summary: SnippetSyncSummary;
|
||
editorSettings?: unknown;
|
||
desktopSettings: DesktopSettings;
|
||
applySummary: WebDavDownloadResult["applySummary"];
|
||
}
|
||
|
||
export interface SnippetTokenStatus {
|
||
hasSavedToken: boolean;
|
||
}
|
||
|
||
export interface SnippetPassphraseStatus {
|
||
hasSavedPassphrase: boolean;
|
||
}
|
||
|
||
export async function webdavSyncTest(config: WebDavConfig): Promise<void> {
|
||
return post("/api/cloud-sync/webdav/test", { config });
|
||
}
|
||
|
||
export async function webdavPasswordStatus(config: WebDavConfig): Promise<WebDavPasswordStatus> {
|
||
return post("/api/cloud-sync/webdav/password-status", { config });
|
||
}
|
||
|
||
export async function saveWebdavSavedPassword(config: WebDavConfig, password: string): Promise<void> {
|
||
return post("/api/cloud-sync/webdav/save-password", { config, password });
|
||
}
|
||
|
||
export async function forgetWebdavSavedPassword(config: WebDavConfig): Promise<void> {
|
||
return post("/api/cloud-sync/webdav/forget-password", { config });
|
||
}
|
||
|
||
export async function webdavSyncSecretsStatus(): Promise<WebDavSyncSecretsStatus> {
|
||
return post("/api/cloud-sync/webdav/sync-secrets-status", {});
|
||
}
|
||
|
||
export async function saveWebdavSyncSecretsPreference(enabled: boolean, passphrase?: string): Promise<void> {
|
||
return post("/api/cloud-sync/webdav/save-sync-secrets-preference", {
|
||
enabled,
|
||
passphrase,
|
||
});
|
||
}
|
||
|
||
export async function forgetWebdavSyncSecretsPassphrase(): Promise<void> {
|
||
return post("/api/cloud-sync/webdav/forget-sync-secrets-passphrase", {});
|
||
}
|
||
|
||
export async function cloudSyncLocalCatalog(editorSettings?: unknown): Promise<SyncSnapshotCatalog> {
|
||
return post("/api/cloud-sync/catalog/local", { editorSettings });
|
||
}
|
||
|
||
export async function localBackupExport(): Promise<never> {
|
||
throw new Error("Local backup is available only in DBX Desktop.");
|
||
}
|
||
|
||
export async function localBackupInspect(): Promise<never> {
|
||
throw new Error("Local backup is available only in DBX Desktop.");
|
||
}
|
||
|
||
export async function localBackupImport(): Promise<never> {
|
||
throw new Error("Local backup is available only in DBX Desktop.");
|
||
}
|
||
|
||
export async function webdavSyncInspect(config: WebDavConfig, secretsPassphrase?: string): Promise<SyncSnapshotCatalog> {
|
||
return post("/api/cloud-sync/webdav/inspect", { config, secretsPassphrase });
|
||
}
|
||
|
||
export async function webdavSyncUpload(config: WebDavConfig, editorSettings?: unknown, secretsPassphrase?: string, includeSecrets = false, selection?: SyncSelection): Promise<WebDavSyncSummary> {
|
||
return post("/api/cloud-sync/webdav/upload", {
|
||
config,
|
||
editorSettings,
|
||
secretsPassphrase,
|
||
includeSecrets,
|
||
selection,
|
||
});
|
||
}
|
||
|
||
export async function webdavSyncDownload(config: WebDavConfig, secretsPassphrase?: string, restoreSecrets = true, selection?: SyncSelection): Promise<WebDavDownloadResult> {
|
||
return post("/api/cloud-sync/webdav/download", { config, secretsPassphrase, restoreSecrets, selection });
|
||
}
|
||
|
||
export async function snippetSyncTest(config: SnippetSyncConfig): Promise<void> {
|
||
await post("/api/cloud-sync/snippet/test", { config });
|
||
}
|
||
|
||
export async function snippetTokenStatus(config: SnippetSyncConfig): Promise<SnippetTokenStatus> {
|
||
return post("/api/cloud-sync/snippet/token-status", { config });
|
||
}
|
||
|
||
export async function saveSnippetSavedToken(config: SnippetSyncConfig, token: string): Promise<void> {
|
||
await post("/api/cloud-sync/snippet/save-token", { config, token });
|
||
}
|
||
|
||
export async function forgetSnippetSavedToken(config: SnippetSyncConfig): Promise<void> {
|
||
await post("/api/cloud-sync/snippet/forget-token", { config });
|
||
}
|
||
|
||
export async function snippetPassphraseStatus(config: SnippetSyncConfig): Promise<SnippetPassphraseStatus> {
|
||
return post("/api/cloud-sync/snippet/passphrase-status", { config });
|
||
}
|
||
|
||
export async function saveSnippetSavedPassphrase(config: SnippetSyncConfig, passphrase: string): Promise<void> {
|
||
await post("/api/cloud-sync/snippet/save-passphrase", { config, passphrase });
|
||
}
|
||
|
||
export async function forgetSnippetSavedPassphrase(config: SnippetSyncConfig): Promise<void> {
|
||
await post("/api/cloud-sync/snippet/forget-passphrase", { config });
|
||
}
|
||
|
||
export async function snippetSyncSettings(provider: SnippetProvider, instanceUrl?: string): Promise<SnippetSyncSettings> {
|
||
return post("/api/cloud-sync/snippet/settings", { provider, instanceUrl });
|
||
}
|
||
|
||
export async function saveSnippetSyncId(provider: SnippetProvider, snippetId?: string, instanceUrl?: string): Promise<void> {
|
||
await post("/api/cloud-sync/snippet/save-id", { provider, snippetId, instanceUrl });
|
||
}
|
||
|
||
export async function retrySnippetLegacyCleanup(config: SnippetSyncConfig): Promise<SnippetSyncSettings> {
|
||
return post("/api/cloud-sync/snippet/retry-legacy-cleanup", { config });
|
||
}
|
||
|
||
export async function snippetSyncInspect(config: SnippetSyncConfig, snippetPassphrase?: string, secretsPassphrase?: string): Promise<SyncSnapshotCatalog> {
|
||
return post("/api/cloud-sync/snippet/inspect", { config, snippetPassphrase, secretsPassphrase });
|
||
}
|
||
|
||
export async function snippetSyncUpload(config: SnippetSyncConfig, editorSettings?: unknown, snippetPassphrase?: string, includeSecrets = false, secretsPassphrase?: string, selection?: SyncSelection, expectedRemoteContentHash?: string): Promise<SnippetSyncSummary> {
|
||
return post("/api/cloud-sync/snippet/upload", {
|
||
config,
|
||
editorSettings,
|
||
snippetPassphrase,
|
||
includeSecrets,
|
||
secretsPassphrase,
|
||
selection,
|
||
expectedRemoteContentHash,
|
||
});
|
||
}
|
||
|
||
export async function snippetSyncDownload(config: SnippetSyncConfig, snippetPassphrase?: string, restoreSecrets = false, secretsPassphrase?: string, selection?: SyncSelection): Promise<SnippetDownloadResult> {
|
||
return post("/api/cloud-sync/snippet/download", {
|
||
config,
|
||
snippetPassphrase,
|
||
restoreSecrets,
|
||
secretsPassphrase,
|
||
selection,
|
||
});
|
||
}
|
||
|
||
export async function loadPinnedTreeNodeIds(): Promise<string[]> {
|
||
return get("/api/app-settings/pinned-tree-node-ids");
|
||
}
|
||
|
||
export async function savePinnedTreeNodeIds(_ids: string[]): Promise<void> {
|
||
return post("/api/app-settings/pinned-tree-node-ids", { ids: _ids });
|
||
}
|
||
|
||
// --- AI Conversations ---
|
||
|
||
export async function saveAiConversation(conversation: AiConversation): Promise<void> {
|
||
return post("/api/ai/conversation", { conversation });
|
||
}
|
||
|
||
export async function loadAiConversations(): Promise<AiConversation[]> {
|
||
return get("/api/ai/conversations");
|
||
}
|
||
|
||
export async function deleteAiConversation(id: string): Promise<void> {
|
||
return del(`/api/ai/conversation/${id}`);
|
||
}
|
||
|
||
// Background AI runs are a Desktop-only capability in this release. Keep the
|
||
// symbols for backend-module parity without changing Web's request-bound SSE
|
||
// lifecycle or adding a partially functional persistence API.
|
||
export async function saveAiRun(_run: AiRun): Promise<void> {
|
||
throw new Error("Background AI runs are only available in DBX Desktop");
|
||
}
|
||
|
||
export async function saveAiRunState(_conversation: AiConversation, _run: AiRun): Promise<void> {
|
||
throw new Error("Background AI runs are only available in DBX Desktop");
|
||
}
|
||
|
||
export async function loadAiRuns(): Promise<AiRun[]> {
|
||
return [];
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Prompt Templates
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function loadPromptTemplates(): Promise<PromptTemplate[]> {
|
||
return get("/api/prompt-templates");
|
||
}
|
||
|
||
export async function savePromptTemplate(id: string, name: string, content: string): Promise<PromptTemplate> {
|
||
return post("/api/prompt-templates", { id, name, content });
|
||
}
|
||
|
||
export async function deletePromptTemplate(id: string): Promise<void> {
|
||
return del(`/api/prompt-templates/${encodeURIComponent(id)}`);
|
||
}
|
||
|
||
export async function getAiGlobalCustomInstructions(): Promise<string> {
|
||
const result = await get<{ content: string }>("/api/prompt-templates/global-instructions");
|
||
return result.content ?? "";
|
||
}
|
||
|
||
export async function setAiGlobalCustomInstructions(content: string): Promise<void> {
|
||
return put("/api/prompt-templates/global-instructions", { content });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// SQL File Execution
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function previewSqlFile(fileOrPath: string | File, encoding?: SqlFileEncoding): Promise<SqlFilePreview> {
|
||
if (typeof fileOrPath === "string") {
|
||
// In web mode a raw path is not useful; throw a clear error
|
||
throw new Error("previewSqlFile in web mode requires a File object, not a file path");
|
||
}
|
||
const formData = new FormData();
|
||
formData.append("file", fileOrPath);
|
||
const res = await fetch(apiUrl(`/api/sql-file/preview${encoding ? `?encoding=${encodeURIComponent(encoding)}` : ""}`), {
|
||
method: "POST",
|
||
body: formData,
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
export async function refreshSqlFilePreview(filePath: string, encoding: SqlFileEncoding): Promise<Pick<SqlFilePreview, "preview" | "canExecuteWithoutSelectedDatabase" | "establishesDatabaseContext">> {
|
||
return post("/api/sql-file/preview/refresh", { filePath, encoding });
|
||
}
|
||
|
||
export async function releaseSqlFilePreview(cleanupToken: string): Promise<void> {
|
||
return post("/api/sql-file/preview/release", { cleanupToken });
|
||
}
|
||
|
||
/**
|
||
* Failure log of one execution for the Web runtime (dbx#11619). The whole-log
|
||
* export streams from the server as an attachment; a selected subset travels as
|
||
* capped text because only the full log has a streaming route.
|
||
*/
|
||
export async function sqlFileFailureLogInfo(executionId: string): Promise<SqlFileFailureLogInfo | null> {
|
||
return get(`/api/sql-file/failure-log/${encodeURIComponent(executionId)}/info`);
|
||
}
|
||
|
||
export async function readSqlFileFailureLogText(executionId: string, format?: string, keys?: SqlFileFailureLogKey[]): Promise<SqlFileFailureLogTextPayload> {
|
||
return post("/api/sql-file/failure-log/text", { executionId, format, keys });
|
||
}
|
||
|
||
export async function exportSqlFileFailureLog(executionId: string, format: string, targetPath: string, keys?: SqlFileFailureLogKey[]): Promise<SqlFileFailureLogExportPayload> {
|
||
if (keys && keys.length > 0) {
|
||
const payload = await readSqlFileFailureLogText(executionId, format, keys);
|
||
if (payload.tooLarge || payload.text === undefined) throw new Error(FAILURE_LOG_TEXT_TOO_LARGE);
|
||
const fileName = targetPath.trim() || `sql-file-failures.${format}`;
|
||
await saveTextFile(payload.text, fileName, format.toUpperCase(), format);
|
||
return { records: keys.length, bytes: payload.text.length, format };
|
||
}
|
||
const anchor = document.createElement("a");
|
||
anchor.href = apiUrl(`/api/sql-file/failure-log/${encodeURIComponent(executionId)}?format=${encodeURIComponent(format)}`);
|
||
anchor.click();
|
||
return { records: 0, bytes: 0, format };
|
||
}
|
||
|
||
export async function executeSqlFile(request: SqlFileRequest): Promise<void> {
|
||
return post("/api/sql-file/execute", { request });
|
||
}
|
||
|
||
export async function inspectSqlFileTables(filePath: string, encoding?: SqlFileEncoding): Promise<SqlFileTable[]> {
|
||
return post("/api/sql-file/tables", { filePath, encoding });
|
||
}
|
||
|
||
export async function executeSqlFiles(request: SqlFileRequest, filePaths: string[]): Promise<void> {
|
||
return post("/api/sql-file/execute", { request, filePaths });
|
||
}
|
||
|
||
export async function cancelSqlFileExecution(executionId: string): Promise<boolean> {
|
||
return post("/api/sql-file/cancel", { executionId });
|
||
}
|
||
|
||
export async function listenSqlFileProgress(_handler: (progress: SqlFileProgress) => void): Promise<() => void> {
|
||
// For HTTP mode we need an executionId, but the tauri API does not take one.
|
||
// The SSE endpoint requires a specific executionId. As a workaround we return
|
||
// a no-op unlisten; callers that need progress in web mode should use
|
||
// the web-specific SQL file progress listener instead.
|
||
return () => {};
|
||
}
|
||
|
||
export async function pendingOpenSqlFiles(): Promise<string[]> {
|
||
return [];
|
||
}
|
||
|
||
export async function pendingOpenDbFiles(): Promise<string[]> {
|
||
return [];
|
||
}
|
||
|
||
export async function pendingOpenConnectionLinks(): Promise<string[]> {
|
||
return [];
|
||
}
|
||
|
||
export async function pendingOpenAiConfigLinks(): Promise<string[]> {
|
||
return [];
|
||
}
|
||
|
||
export async function pendingOpenPluginInstallLinks(): Promise<string[]> {
|
||
return [];
|
||
}
|
||
|
||
export async function readExternalSqlFile(_path: string, _maxSizeBytes?: number): Promise<string> {
|
||
throw new Error("Opening external SQL file paths is only available in the desktop app");
|
||
}
|
||
|
||
export async function readExternalSqlFileSnapshot(_path: string, _maxSizeBytes?: number, _encoding?: string): Promise<import("@/lib/backend/tauri").ExternalSqlFileSnapshot> {
|
||
throw new Error("Opening external SQL file paths is only available in the desktop app");
|
||
}
|
||
|
||
export async function inspectExternalSqlFile(_path: string): Promise<import("@/lib/backend/tauri").ExternalSqlFileStatus> {
|
||
throw new Error("Inspecting external SQL file paths is only available in the desktop app");
|
||
}
|
||
|
||
export async function writeExternalSqlFile(_path: string, _content: string, _options: { expectedContentHash?: string; expectedMissing?: boolean; encoding?: string } = {}): Promise<import("@/lib/backend/tauri").ExternalSqlFileWriteResult> {
|
||
throw new Error("Saving external SQL file paths is only available in the desktop app");
|
||
}
|
||
|
||
export async function saveExternalSqlFile(_defaultFileName: string, _content: string, _filterExtension?: string, _encoding?: string): Promise<{ path: string; version: import("@/types/database").ExternalSqlFileVersion } | null> {
|
||
throw new Error("Saving SQL files locally is only available in the desktop app");
|
||
}
|
||
|
||
export interface SqlFileEntry {
|
||
name: string;
|
||
path: string;
|
||
is_dir: boolean;
|
||
children: SqlFileEntry[];
|
||
}
|
||
|
||
export async function listSqlFilesInFolder(_folderPath: string, _fileFilter?: string, _includeEmptyDirectories?: boolean): Promise<SqlFileEntry[]> {
|
||
throw new Error("Listing SQL files in a folder is only available in the desktop app");
|
||
}
|
||
|
||
export async function searchSqlFileContents(_roots: string[], _query: string, _fileFilter?: string): Promise<import("@/lib/backend/tauri").SqlFileContentSearchResult> {
|
||
throw new Error("Searching SQL file contents is only available in the desktop app");
|
||
}
|
||
|
||
export async function createSqlFileInFolder(_rootPath: string, _directoryPath: string, _fileName: string): Promise<string> {
|
||
throw new Error("Managing SQL files in folders is only available in the desktop app");
|
||
}
|
||
|
||
export async function renameSqlFileInFolder(_rootPath: string, _filePath: string, _fileName: string): Promise<string> {
|
||
throw new Error("Managing SQL files in folders is only available in the desktop app");
|
||
}
|
||
|
||
export async function deleteSqlFileInFolder(_rootPath: string, _filePath: string): Promise<void> {
|
||
throw new Error("Managing SQL files in folders is only available in the desktop app");
|
||
}
|
||
|
||
export async function createSqlFolderInFolder(_rootPath: string, _directoryPath: string, _folderName: string): Promise<string> {
|
||
throw new Error("Managing SQL folders is only available in the desktop app");
|
||
}
|
||
|
||
export async function renameSqlFolderInFolder(_rootPath: string, _folderPath: string, _folderName: string): Promise<string> {
|
||
throw new Error("Managing SQL folders is only available in the desktop app");
|
||
}
|
||
|
||
export async function deleteSqlFolderInFolder(_rootPath: string, _folderPath: string): Promise<void> {
|
||
throw new Error("Managing SQL folders is only available in the desktop app");
|
||
}
|
||
|
||
export interface GlobalSearchRequest {
|
||
roots: string[];
|
||
query: string;
|
||
extensions?: string[];
|
||
caseSensitive?: boolean;
|
||
useRegex?: boolean;
|
||
wholeWord?: boolean;
|
||
limit?: number;
|
||
respectIgnoreRules?: boolean;
|
||
/** Max traversal depth (`WalkBuilder` semantics: root = 0, its entries = 1). */
|
||
maxDepth?: number;
|
||
}
|
||
|
||
export interface GlobalSearchMatch {
|
||
path: string;
|
||
fileName: string;
|
||
line: number;
|
||
column: number;
|
||
matchText: string;
|
||
lineText: string;
|
||
}
|
||
|
||
export async function globalSearch(_request: GlobalSearchRequest): Promise<GlobalSearchMatch[]> {
|
||
throw new Error("Global content search is only available in the desktop app");
|
||
}
|
||
|
||
export interface GlobalSearchSettings {
|
||
roots: string[];
|
||
extensions: string[];
|
||
}
|
||
|
||
export async function loadGlobalSearchSettings(): Promise<GlobalSearchSettings | null> {
|
||
try {
|
||
const raw = globalThis.localStorage?.getItem("dbx-global-search-settings-disk");
|
||
return raw ? (JSON.parse(raw) as GlobalSearchSettings) : null;
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
export async function saveGlobalSearchSettings(settings: GlobalSearchSettings): Promise<void> {
|
||
globalThis.localStorage?.setItem("dbx-global-search-settings-disk", JSON.stringify(settings));
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Data Transfer
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function startTransfer(request: TransferRequest, onProgress: (progress: TransferProgress) => void, onStarted?: () => void): Promise<void> {
|
||
const { operationTimeoutSecs, ...backendRequest } = request;
|
||
// 1. POST to start the transfer
|
||
const res = await fetch(apiUrl("/api/transfer/start"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ request: backendRequest, operationTimeoutSecs }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
onStarted?.();
|
||
|
||
// 2. SSE to listen for progress
|
||
return new Promise((resolve, reject) => {
|
||
const es = new EventSource(apiUrl(`/api/transfer/progress/${request.transferId}`));
|
||
es.onmessage = (e) => {
|
||
const progress: TransferProgress = JSON.parse(e.data);
|
||
onProgress(progress);
|
||
if (isTerminalTransferProgress(progress)) {
|
||
es.close();
|
||
resolve();
|
||
}
|
||
};
|
||
es.onerror = () => {
|
||
es.close();
|
||
reject(new Error("Transfer SSE connection failed"));
|
||
};
|
||
});
|
||
}
|
||
|
||
export async function cancelTransfer(transferId: string): Promise<void> {
|
||
return post("/api/transfer/cancel", { transferId });
|
||
}
|
||
|
||
export async function previewTransferOwnership(request: TransferRequest): Promise<TransferOwnershipPreview> {
|
||
return post("/api/transfer/ownership-preview", { request });
|
||
}
|
||
|
||
export interface SortTablesByFkOptions {
|
||
connectionId: string;
|
||
database: string;
|
||
schema: string;
|
||
tables: string[];
|
||
parentsFirst: boolean;
|
||
}
|
||
|
||
export async function sortTablesByFkDependency(options: SortTablesByFkOptions): Promise<string[]> {
|
||
return post("/api/transfer/sort-tables-by-fk", options);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Table File Import
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function previewTableImportFile(fileOrPath: string | File | TableImportPreviewRequest, options: Partial<TableImportPreviewRequest> = {}): Promise<TableImportPreview> {
|
||
if (typeof fileOrPath === "object" && !(fileOrPath instanceof File)) {
|
||
throw new Error("previewTableImportFile in web mode requires a File object for upload previews");
|
||
}
|
||
if (typeof fileOrPath === "string") {
|
||
if (!options.sourceRef) {
|
||
throw new Error("previewTableImportFile in web mode requires a File object for new uploads");
|
||
}
|
||
const res = await fetch(apiUrl("/api/import/preview-source"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
sourceRef: options.sourceRef,
|
||
connectionId: options.connectionId,
|
||
database: options.database,
|
||
sourceFormat: options.sourceFormat,
|
||
parseOptions: options.parseOptions,
|
||
previewLimit: options.previewLimit,
|
||
}),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
const formData = new FormData();
|
||
formData.append("file", fileOrPath);
|
||
if (options.connectionId) formData.append("connectionId", options.connectionId);
|
||
if (options.database != null) formData.append("database", options.database);
|
||
if (options.sourceFormat) formData.append("sourceFormat", options.sourceFormat);
|
||
if (options.parseOptions) formData.append("parseOptions", JSON.stringify(options.parseOptions));
|
||
if (options.previewLimit != null) formData.append("previewLimit", String(options.previewLimit));
|
||
const res = await fetch(apiUrl("/api/import/preview"), {
|
||
method: "POST",
|
||
body: formData,
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
export async function importTableFile(request: TableImportRequest, onProgress: (progress: TableImportProgress) => void): Promise<TableImportSummary> {
|
||
// 1. POST to start the import
|
||
const res = await fetch(apiUrl("/api/import/execute"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ request }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
|
||
// 2. SSE to listen for progress
|
||
return new Promise((resolve, reject) => {
|
||
const es = new EventSource(apiUrl(`/api/import/progress/${request.importId}`));
|
||
let summary: TableImportSummary | null = null;
|
||
es.onmessage = (e) => {
|
||
const progress: TableImportProgress = JSON.parse(e.data);
|
||
onProgress(progress);
|
||
if (progress.status === "done") {
|
||
summary = {
|
||
importId: progress.importId,
|
||
rowsImported: progress.rowsImported,
|
||
totalRows: progress.totalRows,
|
||
elapsedMs: progress.elapsedMs,
|
||
};
|
||
es.close();
|
||
resolve(summary);
|
||
} else if (progress.status === "error" || progress.status === "cancelled") {
|
||
es.close();
|
||
reject(new Error(progress.error || "Import failed"));
|
||
}
|
||
};
|
||
es.onerror = () => {
|
||
es.close();
|
||
reject(new Error("Import SSE connection failed"));
|
||
};
|
||
});
|
||
}
|
||
|
||
export async function cancelTableImport(importId: string): Promise<boolean> {
|
||
return post("/api/import/cancel", { importId });
|
||
}
|
||
|
||
export async function releaseTableImportSource(sourceRef: string): Promise<boolean> {
|
||
const result = await post<{ released: boolean }>("/api/import/source/release", { sourceRef });
|
||
return result.released;
|
||
}
|
||
|
||
export function inspectMongodbDatabaseDump(connectionId: string, database: string): Promise<MongoDumpCatalog> {
|
||
return post("/api/mongo/dump/catalog", { connectionId, database });
|
||
}
|
||
async function checkMongoUploadSize(files: File[]) {
|
||
const limit = await get<number>("/api/mongo/dump/upload-limit");
|
||
const size = files.reduce((total, file) => total + file.size, 0);
|
||
if (size > limit) throw new Error(`MongoDB upload: ${(size / 1024 ** 3).toFixed(2)} GiB exceeds ${(limit / 1024 ** 3).toFixed(2)} GiB (DBX_MAX_UPLOAD_MB)`);
|
||
}
|
||
|
||
function uploadMongoForm(url: string, form: FormData, options?: MongoSourceReadOptions): Promise<MongoRestoreSourcePreview> {
|
||
return new Promise((resolve, reject) => {
|
||
const xhr = new XMLHttpRequest();
|
||
const abort = () => xhr.abort();
|
||
const cleanup = () => options?.signal?.removeEventListener("abort", abort);
|
||
if (options?.signal?.aborted) {
|
||
reject(new DOMException("Cancelled", "AbortError"));
|
||
return;
|
||
}
|
||
xhr.open("POST", apiUrl(url));
|
||
xhr.upload.onprogress = (event) => options?.onUploadProgress?.(event.loaded, event.lengthComputable ? event.total : 0);
|
||
xhr.onload = async () => {
|
||
cleanup();
|
||
try {
|
||
if (xhr.status < 200 || xhr.status >= 300) throw await backendResponseError(new Response(xhr.responseText, { status: xhr.status }));
|
||
resolve(JSON.parse(xhr.responseText));
|
||
} catch (error) {
|
||
reject(error);
|
||
}
|
||
};
|
||
xhr.onerror = () => {
|
||
cleanup();
|
||
reject(new Error("MongoDB upload connection failed"));
|
||
};
|
||
xhr.onabort = () => {
|
||
cleanup();
|
||
reject(new DOMException("Cancelled", "AbortError"));
|
||
};
|
||
options?.signal?.addEventListener("abort", abort, { once: true });
|
||
xhr.send(form);
|
||
});
|
||
}
|
||
|
||
export async function prepareMongodbRestoreSource(source: MongoDumpSourceInput, format: MongoDumpFormat, gzip: boolean, options?: MongoSourceReadOptions): Promise<MongoRestoreSourcePreview> {
|
||
if (typeof source === "string") throw new Error("Web restores require uploaded files");
|
||
const files = Array.isArray(source) ? source : [source];
|
||
const form = new FormData();
|
||
form.append("format", format);
|
||
form.append("gzip", String(gzip));
|
||
if (format === "directory") {
|
||
form.append("manifest", JSON.stringify(files.map((file) => ({ path: file.webkitRelativePath || file.name, sizeBytes: file.size }))));
|
||
for (const file of files) if (/\.metadata\.json(?:\.gz)?$/.test(file.name)) form.append("file", file, file.webkitRelativePath || file.name);
|
||
} else {
|
||
await checkMongoUploadSize(files);
|
||
for (const file of files) form.append("file", file, file.name);
|
||
return uploadMongoForm("/api/mongo/dump/source", form, options);
|
||
}
|
||
const response = await fetch(apiUrl("/api/mongo/dump/source"), { method: "POST", body: form, signal: options?.signal });
|
||
if (!response.ok) throw await backendResponseError(response);
|
||
return response.json();
|
||
}
|
||
export async function releaseMongodbRestoreSource(sourceRef: string): Promise<boolean> {
|
||
return (await post<{ released: boolean }>("/api/mongo/dump/source/release", { sourceRef })).released;
|
||
}
|
||
async function runMongodbDatabaseTask(mode: "export" | "restore", request: MongoDatabaseDumpRequest | MongoDatabaseRestoreRequest, onProgress: (progress: MongoDatabaseDumpProgress) => void): Promise<MongoDatabaseDumpProgress> {
|
||
await post(`/api/mongo/dump/${mode}`, { request });
|
||
return new Promise((resolve, reject) => {
|
||
const events = new EventSource(apiUrl(`/api/mongo/dump/progress/${request.taskId}`));
|
||
events.onmessage = (event) => {
|
||
const progress: MongoDatabaseDumpProgress = JSON.parse(event.data);
|
||
onProgress(progress);
|
||
if (progress.status === "running") return;
|
||
events.close();
|
||
if (progress.status !== "done") {
|
||
reject(new Error(progress.errorMessage || "MongoDB database task failed"));
|
||
return;
|
||
}
|
||
if (mode === "export") {
|
||
const anchor = document.createElement("a");
|
||
anchor.href = apiUrl(`/api/mongo/export/download/${request.taskId}`);
|
||
anchor.click();
|
||
}
|
||
resolve(progress);
|
||
};
|
||
events.onerror = () => {
|
||
events.close();
|
||
reject(new Error("MongoDB database progress connection failed"));
|
||
};
|
||
});
|
||
}
|
||
export function dumpMongodbDatabase(request: MongoDatabaseDumpRequest, onProgress: (progress: MongoDatabaseDumpProgress) => void) {
|
||
return runMongodbDatabaseTask("export", request, onProgress);
|
||
}
|
||
export async function restoreMongodbDatabase(request: MongoDatabaseRestoreRequest, onProgress: (progress: MongoDatabaseDumpProgress) => void, upload?: MongoRestoreUpload) {
|
||
let acquired: string | undefined;
|
||
try {
|
||
if (upload) {
|
||
await checkMongoUploadSize(upload.files);
|
||
const form = new FormData();
|
||
form.append("format", "directory");
|
||
form.append("gzip", String(upload.gzip));
|
||
form.append("request", JSON.stringify(request));
|
||
for (const file of upload.files) form.append("file", file, file.webkitRelativePath || file.name);
|
||
const started = Date.now();
|
||
const preview = await uploadMongoForm("/api/mongo/dump/source/upload", form, {
|
||
signal: upload.signal,
|
||
onUploadProgress: (loaded, total) =>
|
||
onProgress({
|
||
taskId: request.taskId,
|
||
status: "running",
|
||
phase: "uploading",
|
||
collection: null,
|
||
collectionsDone: 0,
|
||
collectionsTotal: request.collections?.length ?? 0,
|
||
documentsRead: 0,
|
||
documentsWritten: 0,
|
||
documentsFailed: 0,
|
||
indexesCreated: 0,
|
||
elapsedMs: Date.now() - started,
|
||
errorMessage: null,
|
||
filePath: null,
|
||
bytesProcessed: loaded,
|
||
bytesTotal: total,
|
||
}),
|
||
});
|
||
acquired = preview.sourceRef;
|
||
if (upload.signal?.aborted) throw new DOMException("Cancelled", "AbortError");
|
||
request = { ...request, sourceRef: acquired };
|
||
}
|
||
return await runMongodbDatabaseTask("restore", request, onProgress);
|
||
} finally {
|
||
if (acquired) void releaseMongodbRestoreSource(acquired).catch(() => {});
|
||
}
|
||
}
|
||
export async function cancelMongodbDatabaseDump(taskId: string): Promise<boolean> {
|
||
return (await post<{ cancelled: boolean }>("/api/mongo/dump/cancel", { taskId })).cancelled;
|
||
}
|
||
|
||
export async function previewMongodbImportFile(fileOrPath: string | File | MongoImportPreviewRequest, options: Partial<MongoImportPreviewRequest> = {}): Promise<MongoImportPreview> {
|
||
if (typeof fileOrPath === "object" && !(fileOrPath instanceof File)) {
|
||
throw new Error("previewMongodbImportFile in web mode requires a File object for upload previews");
|
||
}
|
||
if (typeof fileOrPath === "string") {
|
||
if (!options.sourceRef) {
|
||
throw new Error("previewMongodbImportFile in web mode requires a File object for new uploads");
|
||
}
|
||
const res = await fetch(apiUrl("/api/mongo/import/preview-source"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
sourceRef: options.sourceRef,
|
||
format: options.format,
|
||
parseOptions: options.parseOptions,
|
||
previewLimit: options.previewLimit,
|
||
}),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
const formData = new FormData();
|
||
formData.append("file", fileOrPath);
|
||
if (options.format) formData.append("format", options.format);
|
||
if (options.parseOptions) formData.append("parseOptions", JSON.stringify(options.parseOptions));
|
||
if (options.previewLimit != null) formData.append("previewLimit", String(options.previewLimit));
|
||
const res = await fetch(apiUrl("/api/mongo/import/preview"), {
|
||
method: "POST",
|
||
body: formData,
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
export async function importMongodbFile(request: MongoImportRequest, onProgress: (progress: MongoImportProgress) => void): Promise<MongoImportSummary> {
|
||
const res = await fetch(apiUrl("/api/mongo/import/execute"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ request }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return new Promise((resolve, reject) => {
|
||
const es = new EventSource(apiUrl(`/api/mongo/import/progress/${request.importId}`));
|
||
es.onmessage = (e) => {
|
||
const progress: MongoImportProgress = JSON.parse(e.data);
|
||
onProgress(progress);
|
||
if (progress.status === "done") {
|
||
es.close();
|
||
resolve({
|
||
importId: progress.importId,
|
||
rowsInserted: progress.rowsInserted,
|
||
rowsFailed: progress.rowsFailed,
|
||
batchesCommitted: progress.batchesCommitted,
|
||
elapsedMs: progress.elapsedMs,
|
||
});
|
||
} else if (progress.status === "error" || progress.status === "cancelled") {
|
||
es.close();
|
||
reject(new Error(progress.errorMessage || "MongoDB import failed"));
|
||
}
|
||
};
|
||
es.onerror = () => {
|
||
es.close();
|
||
reject(new Error("MongoDB import SSE connection failed"));
|
||
};
|
||
});
|
||
}
|
||
|
||
export async function cancelMongodbImport(importId: string): Promise<boolean> {
|
||
return post("/api/mongo/import/cancel", { importId });
|
||
}
|
||
|
||
export async function releaseMongodbImportSource(sourceRef: string): Promise<boolean> {
|
||
const result = await post<{ released: boolean }>("/api/mongo/import/source/release", { sourceRef });
|
||
return result.released;
|
||
}
|
||
|
||
export async function exportMongodbQuery(request: MongoExportRequest, onProgress: (progress: MongoExportProgress) => void): Promise<MongoExportSummary> {
|
||
const res = await fetch(apiUrl("/api/mongo/export"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ request }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return new Promise((resolve, reject) => {
|
||
const es = new EventSource(apiUrl(`/api/mongo/export/progress/${request.exportId}`));
|
||
es.onmessage = (e) => {
|
||
const progress: MongoExportProgress = JSON.parse(e.data);
|
||
onProgress(progress);
|
||
if (progress.status === "done" || progress.status === "error" || progress.status === "cancelled") {
|
||
es.close();
|
||
if (progress.status === "done") {
|
||
const a = document.createElement("a");
|
||
a.href = apiUrl(`/api/mongo/export/download/${request.exportId}`);
|
||
a.click();
|
||
resolve({
|
||
exportId: progress.exportId,
|
||
documentsExported: progress.documentsRead,
|
||
filePath: request.filePath,
|
||
elapsedMs: progress.elapsedMs,
|
||
});
|
||
} else {
|
||
reject(new Error(progress.errorMessage || "MongoDB export failed"));
|
||
}
|
||
}
|
||
};
|
||
es.onerror = () => {
|
||
es.close();
|
||
reject(new Error("MongoDB export SSE connection failed"));
|
||
};
|
||
});
|
||
}
|
||
|
||
export async function cancelMongodbExport(exportId: string): Promise<boolean> {
|
||
return post("/api/mongo/export/cancel", { exportId });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Database Export
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function beginDatabaseBackupSnapshot(_connectionId: string, _database: string, _exportId?: string): Promise<DatabaseBackupSnapshot> {
|
||
throw new Error("Consistent database backup snapshots are only available in the desktop app.");
|
||
}
|
||
|
||
export async function exportDatabaseSql(request: DatabaseExportRequest, onProgress: (progress: ExportProgress) => void): Promise<void> {
|
||
// 1. POST to start the export
|
||
const res = await fetch(apiUrl("/api/export/database"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ request }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
|
||
// 2. SSE to listen for progress
|
||
return new Promise((resolve, reject) => {
|
||
const es = new EventSource(apiUrl(`/api/export/database/progress/${request.exportId}`));
|
||
es.onmessage = (e) => {
|
||
const progress: ExportProgress = JSON.parse(e.data);
|
||
onProgress(progress);
|
||
if (progress.status === "Done" || progress.status === "Error" || progress.status === "Cancelled") {
|
||
es.close();
|
||
if (progress.status === "Done") {
|
||
// Trigger browser download; filename is decided by the server's
|
||
// Content-Disposition header.
|
||
downloadDatabaseExportFile(request.exportId);
|
||
}
|
||
resolve();
|
||
}
|
||
};
|
||
es.onerror = () => {
|
||
es.close();
|
||
reject(new Error("Export SSE connection failed"));
|
||
};
|
||
});
|
||
}
|
||
|
||
function downloadDatabaseExportFile(exportId: string): void {
|
||
const a = document.createElement("a");
|
||
a.href = apiUrl(`/api/export/database/download/${exportId}`);
|
||
a.click();
|
||
}
|
||
|
||
export async function cancelDatabaseExport(exportId: string): Promise<void> {
|
||
await post("/api/export/database/cancel", { exportId });
|
||
}
|
||
|
||
export async function clearDatabaseExportCancellation(_exportId: string): Promise<void> {
|
||
// The web exporter owns and clears its cancellation marker on completion.
|
||
}
|
||
|
||
export async function databaseExportDestinationNeedsConfirmation(_directory: string): Promise<boolean> {
|
||
throw new Error("Scheduled database backups are only available in the desktop app.");
|
||
}
|
||
|
||
export async function recordDatabaseExportDestination(_directory: string): Promise<void> {
|
||
throw new Error("Scheduled database backups are only available in the desktop app.");
|
||
}
|
||
|
||
// --- Table Export ---
|
||
|
||
export async function startTableExport(request: TableExportRequest, onProgress: (progress: TableExportProgress) => void): Promise<TableExportProgress> {
|
||
const { exportId } = request;
|
||
|
||
return new Promise((resolve, reject) => {
|
||
let started = false;
|
||
let settled = false;
|
||
const eventSource = new EventSource(apiUrl(`/api/export/table/progress/${exportId}`));
|
||
|
||
const finish = (callback: () => void) => {
|
||
if (settled) return;
|
||
settled = true;
|
||
eventSource.close();
|
||
callback();
|
||
};
|
||
|
||
eventSource.onopen = () => {
|
||
if (started) return;
|
||
started = true;
|
||
post("/api/export/table", { request }).catch((error) => {
|
||
finish(() => reject(error));
|
||
});
|
||
};
|
||
|
||
eventSource.onmessage = (event) => {
|
||
const progress: TableExportProgress = JSON.parse(event.data);
|
||
onProgress(progress);
|
||
if (progress.status === "Done" || progress.status === "Error" || progress.status === "Cancelled") {
|
||
if (progress.status === "Error") {
|
||
finish(() => reject(new Error(progress.errorMessage || "Export failed")));
|
||
} else if (progress.status === "Done") {
|
||
// Trigger browser download
|
||
downloadTableExportFile(exportId);
|
||
finish(() => resolve(progress));
|
||
} else {
|
||
finish(() => resolve(progress));
|
||
}
|
||
}
|
||
};
|
||
|
||
eventSource.onerror = () => {
|
||
finish(() => reject(new Error("Export progress connection lost")));
|
||
};
|
||
});
|
||
}
|
||
|
||
function downloadTableExportFile(exportId: string): void {
|
||
const a = document.createElement("a");
|
||
a.href = apiUrl(`/api/export/table/download/${exportId}`);
|
||
a.click();
|
||
}
|
||
|
||
// Native dexp/dimp tools only exist next to a local Dameng client install, so
|
||
// the web deployment has no server-side implementation by design.
|
||
const DAMENG_DUMP_WEB_UNSUPPORTED = "Dameng native .dmp export/import requires the desktop application.";
|
||
|
||
export async function startDamengDump(_request: DamengDumpRequest, _onProgress: (progress: DamengDumpProgress) => void): Promise<DamengDumpProgress> {
|
||
throw new Error(DAMENG_DUMP_WEB_UNSUPPORTED);
|
||
}
|
||
|
||
export async function cancelDamengDump(_taskId: string): Promise<void> {
|
||
throw new Error(DAMENG_DUMP_WEB_UNSUPPORTED);
|
||
}
|
||
|
||
export async function resolveDamengDumpTool(_binDir: string, _kind: DamengDumpKind): Promise<string> {
|
||
throw new Error(DAMENG_DUMP_WEB_UNSUPPORTED);
|
||
}
|
||
|
||
export async function cancelTableExport(exportId: string): Promise<void> {
|
||
return post("/api/export/table/cancel", { exportId });
|
||
}
|
||
|
||
export async function startQueryResultExport(request: QueryResultExportRequest, onProgress: (progress: TableExportProgress) => void): Promise<TableExportProgress> {
|
||
const { exportId } = request;
|
||
|
||
return new Promise((resolve, reject) => {
|
||
let started = false;
|
||
let settled = false;
|
||
const eventSource = new EventSource(apiUrl(`/api/export/query-result/progress/${exportId}`));
|
||
|
||
const finish = (callback: () => void) => {
|
||
if (settled) return;
|
||
settled = true;
|
||
eventSource.close();
|
||
callback();
|
||
};
|
||
|
||
eventSource.onopen = () => {
|
||
if (started) return;
|
||
started = true;
|
||
post("/api/export/query-result", { request }).catch((error) => {
|
||
finish(() => reject(error));
|
||
});
|
||
};
|
||
|
||
eventSource.onmessage = (event) => {
|
||
const progress: TableExportProgress = JSON.parse(event.data);
|
||
onProgress(progress);
|
||
if (progress.status === "Done" || progress.status === "Error" || progress.status === "Cancelled") {
|
||
if (progress.status === "Error") {
|
||
finish(() => reject(new Error(progress.errorMessage || "Export failed")));
|
||
} else if (progress.status === "Done") {
|
||
downloadQueryResultExportFile(exportId);
|
||
finish(() => resolve(progress));
|
||
} else {
|
||
finish(() => resolve(progress));
|
||
}
|
||
}
|
||
};
|
||
|
||
eventSource.onerror = () => {
|
||
finish(() => reject(new Error("Export progress connection lost")));
|
||
};
|
||
});
|
||
}
|
||
|
||
function downloadQueryResultExportFile(exportId: string): void {
|
||
const a = document.createElement("a");
|
||
a.href = apiUrl(`/api/export/query-result/download/${exportId}`);
|
||
a.click();
|
||
}
|
||
|
||
export async function cancelQueryResultExport(exportId: string, executionId?: string): Promise<void> {
|
||
return post("/api/export/query-result/cancel", {
|
||
exportId,
|
||
...(executionId ? { executionId } : {}),
|
||
});
|
||
}
|
||
|
||
export async function createQueryResultTempFile(_extension = "xlsx"): Promise<string> {
|
||
throw new Error("Opening query results in an external application is only available in the desktop app");
|
||
}
|
||
|
||
export async function exportQueryResultCsv(filePath: string, columns: string[], rows: readonly (readonly XlsxCellValue[])[], csvQuoteMode: CsvQuoteMode = "all", nullLiteral?: string): Promise<void> {
|
||
const { formatCsv } = await import("@/lib/export/exportFormats");
|
||
const content = formatCsv(columns, rows as (string | number | boolean | null)[][], csvQuoteMode, nullLiteral);
|
||
const fileName = filePath.split(/[\\/]/).pop() || "export.csv";
|
||
const blob = new Blob(["\uFEFF", content], {
|
||
type: "text/csv;charset=utf-8",
|
||
});
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = fileName;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
export async function exportTableDataCsv(_options: TableCsvExportOptions): Promise<number> {
|
||
throw new Error("Streaming table CSV export is only available in the desktop runtime");
|
||
}
|
||
|
||
function downloadTextFile(filePath: string, fallbackFileName: string, content: string, mimeType: string): void {
|
||
const fileName = filePath.split(/[\\/]/).pop() || fallbackFileName;
|
||
const blob = new Blob(["\uFEFF", content], { type: mimeType });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = fileName;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
export async function exportQueryResultXlsx(
|
||
filePath: string,
|
||
sheetName: string | undefined,
|
||
columns: string[],
|
||
columnTypes: string[],
|
||
columnComments: readonly (string | null)[] | undefined,
|
||
rows: readonly (readonly XlsxCellValue[])[],
|
||
numericColumnRightAlign?: boolean,
|
||
autoFilter?: boolean,
|
||
// Accepted for signature parity with the Tauri backend but unused: the web
|
||
// builder writes every cell as `inlineStr`, so a temporal value keeps the
|
||
// text the grid already formatted (milliseconds included) and never goes
|
||
// through a `numFmt` display pattern.
|
||
_dateTimeFormat?: string,
|
||
headerCommentRows?: boolean,
|
||
): Promise<void> {
|
||
const { buildXlsxWorkbook } = await import("@/lib/export/xlsxExport");
|
||
const workbook = buildXlsxWorkbook({
|
||
sheetName: sheetName || "Export",
|
||
columns,
|
||
columnTypes,
|
||
columnComments,
|
||
rows,
|
||
numericColumnRightAlign,
|
||
autoFilter,
|
||
headerCommentRows,
|
||
});
|
||
const fileName = filePath.split(/[\\/]/).pop() || "export.xlsx";
|
||
const blob = new Blob([new Uint8Array(workbook)], {
|
||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||
});
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = fileName;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
export async function exportQueryResultsXlsx(
|
||
filePath: string,
|
||
worksheets: readonly {
|
||
sheetName?: string;
|
||
columns: readonly string[];
|
||
columnTypes?: readonly string[];
|
||
columnComments?: readonly (string | null)[];
|
||
rows: readonly (readonly XlsxCellValue[])[];
|
||
numericColumnRightAlign?: boolean;
|
||
autoFilter?: boolean;
|
||
headerCommentRows?: boolean;
|
||
}[],
|
||
autoFilter?: boolean,
|
||
_dateTimeFormat?: string,
|
||
): Promise<void> {
|
||
const { buildXlsxWorkbookMulti } = await import("@/lib/export/xlsxExport");
|
||
const workbook = buildXlsxWorkbookMulti(autoFilter === undefined ? worksheets : worksheets.map((worksheet) => ({ ...worksheet, autoFilter: worksheet.autoFilter ?? autoFilter })));
|
||
const fileName = filePath.split(/[\\/]/).pop() || "export.xlsx";
|
||
const blob = new Blob([new Uint8Array(workbook)], {
|
||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||
});
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = fileName;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
export async function exportQueryResultJson(filePath: string, columns: string[], rows: readonly (readonly XlsxCellValue[])[]): Promise<void> {
|
||
const result = await post<{ content: string }>("/api/export/query-result-json", { columns, rows });
|
||
downloadTextFile(filePath, "export.json", result.content, "application/json;charset=utf-8");
|
||
}
|
||
|
||
export async function exportQueryResultMarkdown(filePath: string, columns: string[], rows: readonly (readonly XlsxCellValue[])[]): Promise<void> {
|
||
const result = await post<{ content: string }>("/api/export/query-result-markdown", { columns, rows });
|
||
downloadTextFile(filePath, "export.md", result.content, "text/markdown;charset=utf-8");
|
||
}
|
||
|
||
export async function exportQueryResultHtml(filePath: string, title: string | undefined, columns: string[], rows: readonly (readonly XlsxCellValue[])[]): Promise<void> {
|
||
const result = await post<{ content: string }>("/api/export/query-result-html", { title, columns, rows });
|
||
downloadTextFile(filePath, "export.html", result.content, "text/html;charset=utf-8");
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Redis
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function redisListDatabases(connectionId: string): Promise<RedisDatabaseInfo[]> {
|
||
return post("/api/redis/list-databases", { connectionId });
|
||
}
|
||
|
||
export async function redisScanKeys(connectionId: string, db: number, cursor: number, pattern: string, count: number): Promise<RedisScanResult> {
|
||
return post("/api/redis/scan-keys", {
|
||
connectionId,
|
||
db,
|
||
cursor,
|
||
pattern,
|
||
count,
|
||
});
|
||
}
|
||
|
||
export async function redisScanKeysBatch(connectionId: string, db: number, cursor: number, pattern: string, count: number, maxIterations: number, includeTypes = true): Promise<RedisScanResult> {
|
||
return post("/api/redis/scan-keys-batch", {
|
||
connectionId,
|
||
db,
|
||
cursor,
|
||
pattern,
|
||
count,
|
||
maxIterations,
|
||
includeTypes,
|
||
});
|
||
}
|
||
|
||
export async function redisScanValues(connectionId: string, db: number, cursor: number, pattern: string, query: string, count: number, includeKeyMatches = false): Promise<RedisScanResult> {
|
||
return post("/api/redis/scan-values", {
|
||
connectionId,
|
||
db,
|
||
cursor,
|
||
pattern,
|
||
query,
|
||
includeKeyMatches,
|
||
count,
|
||
});
|
||
}
|
||
|
||
export async function redisGetValue(connectionId: string, db: number, keyRaw: string, previewLimitBytes?: number): Promise<RedisValue> {
|
||
return post("/api/redis/get-value", { connectionId, db, keyRaw, previewLimitBytes });
|
||
}
|
||
|
||
export async function redisGetRawValue(connectionId: string, db: number, keyRaw: string): Promise<RedisBlob> {
|
||
return post("/api/redis/get-raw-value", { connectionId, db, keyRaw });
|
||
}
|
||
|
||
export async function redisGetTtl(connectionId: string, db: number, keyRaw: string): Promise<number> {
|
||
return post("/api/redis/get-ttl", { connectionId, db, keyRaw });
|
||
}
|
||
|
||
export async function redisGetStreamEntries(connectionId: string, db: number, keyRaw: string, cursor?: string): Promise<RedisStreamPage> {
|
||
return post("/api/redis/get-stream-entries", { connectionId, db, keyRaw, cursor });
|
||
}
|
||
|
||
export async function redisGetStreamGroups(connectionId: string, db: number, keyRaw: string): Promise<RedisStreamGroup[]> {
|
||
return post("/api/redis/get-stream-groups", { connectionId, db, keyRaw });
|
||
}
|
||
|
||
export async function redisGetStreamConsumers(connectionId: string, db: number, keyRaw: string, groupRaw: string): Promise<RedisStreamConsumer[]> {
|
||
return post("/api/redis/get-stream-consumers", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
groupRaw,
|
||
});
|
||
}
|
||
|
||
export async function redisGetStreamPending(connectionId: string, db: number, keyRaw: string, groupRaw: string, cursor?: string, consumerRaw?: string): Promise<RedisStreamPendingPage> {
|
||
return post("/api/redis/get-stream-pending", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
groupRaw,
|
||
cursor,
|
||
...(consumerRaw === undefined ? {} : { consumerRaw }),
|
||
});
|
||
}
|
||
|
||
export async function redisSetString(connectionId: string, db: number, keyRaw: string, value: string, ttl?: number): Promise<void> {
|
||
return post("/api/redis/set-string", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
value,
|
||
ttl,
|
||
});
|
||
}
|
||
|
||
export async function redisDeleteKey(connectionId: string, db: number, keyRaw: string): Promise<void> {
|
||
return post("/api/redis/delete-key", { connectionId, db, keyRaw });
|
||
}
|
||
|
||
export async function redisRenameKey(connectionId: string, db: number, keyRaw: string, newKeyRaw: string): Promise<void> {
|
||
return post("/api/redis/rename-key", { connectionId, db, keyRaw, newKeyRaw });
|
||
}
|
||
|
||
export async function redisHashSet(connectionId: string, db: number, keyRaw: string, field: string, value: string, ttl?: number): Promise<void> {
|
||
return post("/api/redis/hash-set", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
field,
|
||
value,
|
||
ttl,
|
||
});
|
||
}
|
||
|
||
export async function redisHashDel(connectionId: string, db: number, keyRaw: string, field: string): Promise<void> {
|
||
return post("/api/redis/hash-del", { connectionId, db, keyRaw, field });
|
||
}
|
||
|
||
export async function redisHashFieldUpdate(connectionId: string, db: number, keyRaw: string, oldField: string, newField: string, value: string): Promise<void> {
|
||
return post("/api/redis/hash-field-update", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
oldField,
|
||
newField,
|
||
value,
|
||
});
|
||
}
|
||
|
||
export async function redisHashFieldSetTtl(connectionId: string, db: number, keyRaw: string, field: string, ttl: number): Promise<void> {
|
||
return post("/api/redis/hash-field-set-ttl", { connectionId, db, keyRaw, field, ttl });
|
||
}
|
||
|
||
export async function redisHashFieldSetExpireAt(connectionId: string, db: number, keyRaw: string, field: string, expireAt: number): Promise<void> {
|
||
return post("/api/redis/hash-field-set-expire-at", { connectionId, db, keyRaw, field, expireAt });
|
||
}
|
||
|
||
export async function redisListPush(connectionId: string, db: number, keyRaw: string, value: string, ttl?: number, side?: "left" | "right"): Promise<void> {
|
||
return post("/api/redis/list-push", { connectionId, db, keyRaw, value, ttl, side });
|
||
}
|
||
|
||
export async function redisListSet(connectionId: string, db: number, keyRaw: string, index: number, value: string): Promise<void> {
|
||
return post("/api/redis/list-set", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
index,
|
||
value,
|
||
});
|
||
}
|
||
|
||
export async function redisListRemove(connectionId: string, db: number, keyRaw: string, index: number): Promise<void> {
|
||
return post("/api/redis/list-remove", { connectionId, db, keyRaw, index });
|
||
}
|
||
|
||
export async function redisSetAdd(connectionId: string, db: number, keyRaw: string, member: string, ttl?: number): Promise<void> {
|
||
return post("/api/redis/set-add", { connectionId, db, keyRaw, member, ttl });
|
||
}
|
||
|
||
export async function redisSetRemove(connectionId: string, db: number, keyRaw: string, member: string): Promise<void> {
|
||
return post("/api/redis/set-remove", { connectionId, db, keyRaw, member });
|
||
}
|
||
|
||
export async function redisZadd(connectionId: string, db: number, keyRaw: string, member: string, score: number, ttl?: number): Promise<void> {
|
||
return post("/api/redis/zadd", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
member,
|
||
score,
|
||
ttl,
|
||
});
|
||
}
|
||
|
||
export async function redisZrem(connectionId: string, db: number, keyRaw: string, member: string): Promise<void> {
|
||
return post("/api/redis/zrem", { connectionId, db, keyRaw, member });
|
||
}
|
||
|
||
export async function redisZsetUpdate(connectionId: string, db: number, keyRaw: string, originalMember: string, expectedScore: string, member: string, score: string): Promise<boolean> {
|
||
return post("/api/redis/zset-update", { connectionId, db, keyRaw, originalMember, expectedScore, member, score });
|
||
}
|
||
|
||
export async function redisStreamAdd(connectionId: string, db: number, keyRaw: string, entryId: string, fields: [string, string][], ttl?: number): Promise<void> {
|
||
return post("/api/redis/stream-add", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
entryId,
|
||
fields,
|
||
ttl,
|
||
});
|
||
}
|
||
|
||
export async function redisJsonSet(connectionId: string, db: number, keyRaw: string, value: string, ttl?: number): Promise<void> {
|
||
return post("/api/redis/json-set", { connectionId, db, keyRaw, value, ttl });
|
||
}
|
||
|
||
export async function redisCheckJsonModule(connectionId: string, db: number): Promise<boolean> {
|
||
return post("/api/redis/check-json-module", { connectionId, db });
|
||
}
|
||
|
||
export async function redisSetTtl(connectionId: string, db: number, keyRaw: string, ttl: number): Promise<void> {
|
||
return post("/api/redis/set-ttl", { connectionId, db, keyRaw, ttl });
|
||
}
|
||
|
||
export async function redisSetExpireAt(connectionId: string, db: number, keyRaw: string, expireAt: number): Promise<void> {
|
||
return post("/api/redis/set-expire-at", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
expireAt,
|
||
});
|
||
}
|
||
|
||
export async function redisSetKeysTtl(connectionId: string, db: number, keyRaws: string[], ttl: number): Promise<RedisKeysExpiryResult> {
|
||
return post("/api/redis/set-keys-ttl", { connectionId, db, keyRaws, ttl });
|
||
}
|
||
|
||
export async function redisSetKeysExpireAt(connectionId: string, db: number, keyRaws: string[], expireAt: number): Promise<RedisKeysExpiryResult> {
|
||
return post("/api/redis/set-keys-expire-at", { connectionId, db, keyRaws, expireAt });
|
||
}
|
||
|
||
export async function redisDeleteKeys(connectionId: string, db: number, keyRaws: string[]): Promise<number> {
|
||
return post("/api/redis/delete-keys", { connectionId, db, keyRaws });
|
||
}
|
||
|
||
export async function redisDeleteKeysByPattern(connectionId: string, db: number, pattern: string): Promise<number> {
|
||
return post("/api/redis/delete-keys-by-pattern", { connectionId, db, pattern });
|
||
}
|
||
|
||
export async function redisFlushDb(connectionId: string, db: number): Promise<void> {
|
||
return post("/api/redis/flush-db", { connectionId, db });
|
||
}
|
||
|
||
export async function redisExecuteCommand(connectionId: string, db: number, command: string, skipSafetyCheck?: boolean): Promise<RedisCommandResult> {
|
||
return post("/api/redis/execute-command", {
|
||
connectionId,
|
||
db,
|
||
command,
|
||
skipSafetyCheck: skipSafetyCheck ?? false,
|
||
});
|
||
}
|
||
|
||
export async function redisLoadMore(connectionId: string, db: number, keyRaw: string, keyType: string, cursor: number, count: number, filter?: string, sortDirection?: "asc" | "desc"): Promise<RedisCollectionPage> {
|
||
return post("/api/redis/load-more", {
|
||
connectionId,
|
||
db,
|
||
keyRaw,
|
||
keyType,
|
||
cursor,
|
||
count,
|
||
filter,
|
||
sortDirection,
|
||
});
|
||
}
|
||
|
||
export async function redisPubSubPublish(connectionId: string, db: number, channel: string, message: string): Promise<{ subscribers: number }> {
|
||
return post("/api/redis/pubsub/publish", {
|
||
connectionId,
|
||
db,
|
||
channel,
|
||
message,
|
||
});
|
||
}
|
||
|
||
export async function redisPubSubConnect(connectionId: string, monitor = false): Promise<WebSocket> {
|
||
return new WebSocket(apiWebSocketUrl(`/api/redis/pubsub/ws?connectionId=${encodeURIComponent(connectionId)}&monitor=${monitor}`));
|
||
}
|
||
|
||
export async function redisSlowlogGet(connectionId: string, count: number, nodeHost?: string, nodePort?: number): Promise<RedisSlowlogEntry[]> {
|
||
return post("/api/redis/slowlog-get", {
|
||
connectionId,
|
||
count,
|
||
nodeHost,
|
||
nodePort,
|
||
});
|
||
}
|
||
|
||
export async function redisClusterMasterNodes(connectionId: string): Promise<RedisNodeEndpoint[]> {
|
||
return post("/api/redis/cluster-master-nodes", { connectionId });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// etcd
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function etcdListPrefix(connectionId: string, prefix: string, limit: number, continuation?: string | null, options?: KvListPrefixOptions | null): Promise<KvListPrefixResponse> {
|
||
return post("/api/etcd/list-prefix", {
|
||
connectionId,
|
||
prefix,
|
||
limit,
|
||
continuation,
|
||
revision: options?.revision ?? null,
|
||
includeValues: options?.includeValues ?? null,
|
||
});
|
||
}
|
||
|
||
export async function etcdSupportsTtl(connectionId: string): Promise<boolean> {
|
||
return post("/api/etcd/supports-ttl", { connectionId });
|
||
}
|
||
|
||
export async function etcdGet(connectionId: string, key: string, options?: KvGetOptions | null): Promise<KvGetResponse> {
|
||
return post("/api/etcd/get", {
|
||
connectionId,
|
||
key,
|
||
keyBytes: options?.keyBytes ?? null,
|
||
revision: options?.revision ?? null,
|
||
metadataOnly: options?.metadataOnly ?? null,
|
||
});
|
||
}
|
||
|
||
export async function etcdPut(connectionId: string, key: string, value: KvValue, options?: KvPutOptions | number | null): Promise<KvPutResponse> {
|
||
const legacyLease = typeof options === "number" ? options : null;
|
||
const putOptions = typeof options === "object" ? options : null;
|
||
return post("/api/etcd/put", {
|
||
connectionId,
|
||
key,
|
||
value,
|
||
lease: legacyLease ?? putOptions?.lease ?? null,
|
||
ttl: putOptions?.ttl ?? null,
|
||
preserveLease: putOptions?.preserveLease ?? null,
|
||
keyBytes: putOptions?.keyBytes ?? null,
|
||
expectedModRevision: putOptions?.expectedModRevision ?? null,
|
||
expectedCreateRevision: putOptions?.expectedCreateRevision ?? null,
|
||
});
|
||
}
|
||
|
||
export async function etcdDelete(connectionId: string, key: string, options?: KvDeleteOptions | null): Promise<KvDeleteResponse> {
|
||
return post("/api/etcd/delete", {
|
||
connectionId,
|
||
key,
|
||
keyBytes: options?.keyBytes ?? null,
|
||
expectedModRevision: options?.expectedModRevision ?? null,
|
||
});
|
||
}
|
||
|
||
export async function etcdRename(
|
||
connectionId: string,
|
||
request: {
|
||
key: string;
|
||
keyBytes?: KvValue | null;
|
||
newKey: string;
|
||
expectedModRevision?: KvInt64 | null;
|
||
},
|
||
): Promise<{ renamed: boolean; revision?: KvInt64 | null }> {
|
||
return post("/api/etcd/rename", { connectionId, request });
|
||
}
|
||
|
||
export async function etcdHistory(
|
||
connectionId: string,
|
||
request: {
|
||
key: string;
|
||
keyBytes?: KvValue | null;
|
||
startRevision?: KvInt64 | null;
|
||
endRevision?: KvInt64 | null;
|
||
limit: number;
|
||
},
|
||
): Promise<KvHistoryResponse> {
|
||
return post("/api/etcd/history", { connectionId, request });
|
||
}
|
||
|
||
export async function etcdStatus(connectionId: string): Promise<KvStatusResponse> {
|
||
return post("/api/etcd/status", { connectionId });
|
||
}
|
||
export async function etcdPreflight(connectionId: string, action: string, params: Record<string, unknown>): Promise<import("./tauri").EtcdPreflightResponse> {
|
||
return post("/api/etcd/preflight", { connectionId, request: { action, params } });
|
||
}
|
||
export async function etcdCompact(connectionId: string, revision: KvInt64, approval: import("./tauri").EtcdDangerousApproval): Promise<{ revision: KvInt64 }> {
|
||
return post("/api/etcd/compact", { connectionId, revision, ...approval });
|
||
}
|
||
export async function etcdDefrag(connectionId: string, endpoints: string[], approval: import("./tauri").EtcdDangerousApproval): Promise<EtcdDefragResponse> {
|
||
return post("/api/etcd/defrag", { connectionId, endpoints, ...approval });
|
||
}
|
||
export async function etcdWatchStart(connectionId: string, request: EtcdWatchStartRequest): Promise<EtcdWatchStartResponse> {
|
||
return post("/api/etcd/watch/start", { connectionId, request });
|
||
}
|
||
export async function etcdWatchPoll(connectionId: string, watchId: string): Promise<EtcdWatchPollResponse> {
|
||
return post("/api/etcd/watch/poll", { connectionId, watchId });
|
||
}
|
||
export async function etcdWatchStop(connectionId: string, watchId: string): Promise<{ stopped: boolean }> {
|
||
return post("/api/etcd/watch/stop", { connectionId, watchId });
|
||
}
|
||
export async function etcdLeaseList(connectionId: string, limit = 100, continuation?: string | null): Promise<EtcdLeaseListResponse> {
|
||
return post("/api/etcd/lease/list", { connectionId, limit, continuation: continuation ?? null });
|
||
}
|
||
export async function etcdLeaseCall<T = unknown>(connectionId: string, operation: "get" | "grant" | "keepalive" | "revoke", params: Record<string, unknown>, approval?: import("./tauri").EtcdDangerousApproval): Promise<T> {
|
||
return post("/api/etcd/lease/call", { connectionId, operation, params, ...approval });
|
||
}
|
||
export async function etcdAuthCall<T = unknown>(connectionId: string, operation: string, params: Record<string, unknown>, approval?: import("./tauri").EtcdDangerousApproval): Promise<T> {
|
||
return post("/api/etcd/auth/call", { connectionId, operation, params, ...approval });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// ZooKeeper
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function zookeeperListPrefix(connectionId: string, prefix: string, limit: number, continuation?: string | null, options?: KvListPrefixOptions | null): Promise<KvListPrefixResponse> {
|
||
return post("/api/zookeeper/list-prefix", {
|
||
connectionId,
|
||
prefix,
|
||
limit,
|
||
continuation,
|
||
recursive: options?.recursive ?? null,
|
||
});
|
||
}
|
||
|
||
export async function zookeeperGet(connectionId: string, key: string): Promise<KvGetResponse> {
|
||
return post("/api/zookeeper/get", { connectionId, key });
|
||
}
|
||
|
||
export async function zookeeperPut(connectionId: string, key: string, value: KvValue, options?: KvPutOptions | null): Promise<KvPutResponse> {
|
||
return post("/api/zookeeper/put", {
|
||
connectionId,
|
||
key,
|
||
value,
|
||
options: options ?? null,
|
||
});
|
||
}
|
||
|
||
export async function zookeeperDelete(connectionId: string, key: string): Promise<KvDeleteResponse> {
|
||
return post("/api/zookeeper/delete", { connectionId, key });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Consul
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function consulCapabilities(connectionId: string): Promise<import("@/types/consul").ConsulCapabilities> {
|
||
return post("/api/consul/capabilities", { connectionId });
|
||
}
|
||
|
||
export async function consulTxn(connectionId: string, request: import("@/types/consul").ConsulTxnRequest): Promise<import("@/types/consul").ConsulTxnResult> {
|
||
return post("/api/consul/txn", { connectionId, request });
|
||
}
|
||
|
||
export async function consulRenameKey(connectionId: string, source: string, target: string, expectedModifyIndex: KvInt64, copy = false): Promise<import("@/types/consul").ConsulTxnResult> {
|
||
return post("/api/consul/rename-key", { connectionId, source, target, expectedModifyIndex, copy });
|
||
}
|
||
|
||
export async function consulBlockingQuery(connectionId: string, request: import("@/types/consul").ConsulBlockingRequest): Promise<import("@/types/consul").ConsulBlockingResponse> {
|
||
return post("/api/consul/blocking-query", { connectionId, request });
|
||
}
|
||
|
||
export function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest & { target: { kind: "catalogServices" } }): Promise<import("@/types/consul").ConsulDomainWatchResponse<Record<string, string[]>>>;
|
||
export function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest & { target: { kind: "catalogNodes" } }): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulCatalogNode[]>>;
|
||
export function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest & { target: { kind: "catalogServiceNodes"; service: string } }): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulCatalogServiceNode[]>>;
|
||
export function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest & { target: { kind: "catalogNodeServices"; node: string } }): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulNodeServices>>;
|
||
export function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest & { target: { kind: "healthNode"; node: string } }): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulHealthCheck[]>>;
|
||
export function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest & { target: { kind: "healthServiceChecks"; service: string } }): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulHealthCheck[]>>;
|
||
export function consulDomainWatch(
|
||
connectionId: string,
|
||
request: import("@/types/consul").ConsulDomainWatchRequest & { target: { kind: "healthServiceInstances"; service: string; passing: boolean | null } },
|
||
): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulServiceInstance[]>>;
|
||
export function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest & { target: { kind: "healthState"; state: string } }): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulHealthCheck[]>>;
|
||
export function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulDomainWatchItems>>;
|
||
export async function consulDomainWatch(connectionId: string, request: import("@/types/consul").ConsulDomainWatchRequest): Promise<import("@/types/consul").ConsulDomainWatchResponse<import("@/types/consul").ConsulDomainWatchItems>> {
|
||
return post("/api/consul/domain-watch", { connectionId, request });
|
||
}
|
||
|
||
export async function consulCancelBlocking(connectionId: string, scope: import("@/types/consul").ConsulScope, generation: number, operationId: string): Promise<boolean> {
|
||
return post("/api/consul/cancel-blocking", { connectionId, scope, generation, operationId });
|
||
}
|
||
|
||
export async function consulWatchStart(connectionId: string, request: import("@/types/consul").ConsulBlockingRequest): Promise<string> {
|
||
await consulBlockingQuery(connectionId, request);
|
||
return request.operationId;
|
||
}
|
||
|
||
export async function consulListRecursive(connectionId: string, prefix: string, maxEntries = 10_000, maxValueBytes = 32 * 1024 * 1024): Promise<import("@/types/consul").ConsulRecursiveListResponse> {
|
||
return post("/api/consul/list-recursive", { connectionId, prefix, maxEntries, maxValueBytes });
|
||
}
|
||
|
||
export async function consulSearch(connectionId: string, request: import("@/types/consul").ConsulSearchRequest): Promise<import("@/types/consul").ConsulSearchResponse> {
|
||
return post("/api/consul/search", { connectionId, request });
|
||
}
|
||
|
||
export async function consulSearchProgress(connectionId: string, requestId: string, scope: import("@/types/consul").ConsulScope, generation: number): Promise<import("@/types/consul").ConsulSearchProgress> {
|
||
return post("/api/consul/search-progress", { connectionId, requestId, scope, generation });
|
||
}
|
||
|
||
export async function consulCancelSearch(connectionId: string, requestId: string, scope: import("@/types/consul").ConsulScope, generation: number): Promise<boolean> {
|
||
return post("/api/consul/cancel-search", { connectionId, requestId, scope, generation });
|
||
}
|
||
|
||
export async function consulExportBundle(connectionId: string, request: import("@/types/consul").ConsulExportRequest): Promise<import("@/types/consul").ConsulKvBundle> {
|
||
return post("/api/consul/export-bundle", { connectionId, request });
|
||
}
|
||
|
||
export async function consulImportPreview(connectionId: string, request: import("@/types/consul").ConsulImportRequest): Promise<import("@/types/consul").ConsulImportPreview> {
|
||
return post("/api/consul/import-preview", { connectionId, request });
|
||
}
|
||
|
||
export async function consulImportExecute(connectionId: string, request: import("@/types/consul").ConsulImportRequest): Promise<import("@/types/consul").ConsulImportReport> {
|
||
return post("/api/consul/import-execute", { connectionId, request });
|
||
}
|
||
|
||
export async function consulDeletePrefixPreview(connectionId: string, prefix: string): Promise<import("@/types/consul").ConsulDeletePrefixPreview> {
|
||
return post("/api/consul/delete-prefix-preview", { connectionId, prefix });
|
||
}
|
||
|
||
export async function consulDeletePrefixExecute(connectionId: string, request: import("@/types/consul").ConsulDeletePrefixRequest): Promise<import("@/types/consul").ConsulDeletePrefixReport> {
|
||
return post("/api/consul/delete-prefix-execute", { connectionId, request });
|
||
}
|
||
|
||
export async function consulListPrefix(connectionId: string, prefix: string, limit: number, continuation?: string | null): Promise<KvListPrefixResponse> {
|
||
return post("/api/consul/list-prefix", { connectionId, prefix, limit, continuation: continuation ?? null });
|
||
}
|
||
|
||
export async function consulGet(connectionId: string, key: string): Promise<KvGetResponse> {
|
||
return post("/api/consul/get", { connectionId, key });
|
||
}
|
||
|
||
export async function consulPut(connectionId: string, key: string, value: KvValue, options?: KvPutOptions | null): Promise<KvPutResponse> {
|
||
return post("/api/consul/put", { connectionId, key, value, options: options ?? null });
|
||
}
|
||
|
||
export async function consulDelete(connectionId: string, key: string, options?: KvDeleteOptions | null): Promise<KvDeleteResponse> {
|
||
return post("/api/consul/delete", { connectionId, key, options: options ?? null });
|
||
}
|
||
|
||
export async function consulPreparedQueryList(connectionId: string): Promise<import("@/types/consul").ConsulPreparedQuery[]> {
|
||
return post("/api/consul/prepared-query/list", { connectionId });
|
||
}
|
||
export async function consulPreparedQueryRead(connectionId: string, id: string): Promise<import("@/types/consul").ConsulPreparedQuery> {
|
||
return post("/api/consul/prepared-query/read", { connectionId, id });
|
||
}
|
||
export async function consulPreparedQueryCreate(connectionId: string, input: import("@/types/consul").ConsulPreparedQueryInput): Promise<string> {
|
||
return post("/api/consul/prepared-query/create", { connectionId, input });
|
||
}
|
||
export async function consulPreparedQueryUpdate(connectionId: string, id: string, input: import("@/types/consul").ConsulPreparedQueryInput): Promise<void> {
|
||
await post("/api/consul/prepared-query/update", { connectionId, id, input });
|
||
}
|
||
export async function consulPreparedQueryDelete(connectionId: string, id: string): Promise<void> {
|
||
await post("/api/consul/prepared-query/delete", { connectionId, id });
|
||
}
|
||
export async function consulPreparedQueryExecute(connectionId: string, request: import("@/types/consul").ConsulPreparedQueryExecuteRequest): Promise<import("@/types/consul").ConsulPreparedQueryExecuteResponse> {
|
||
return post("/api/consul/prepared-query/execute", { connectionId, request });
|
||
}
|
||
export async function consulPreparedQueryExplain(connectionId: string, query: string): Promise<unknown> {
|
||
return post("/api/consul/prepared-query/explain", { connectionId, id: query });
|
||
}
|
||
export async function consulEventList(connectionId: string, name?: string | null): Promise<import("@/types/consul").ConsulEvent[]> {
|
||
return post("/api/consul/event/list", { connectionId, name: name ?? null });
|
||
}
|
||
export async function consulEventFire(connectionId: string, request: import("@/types/consul").ConsulEventFireRequest): Promise<import("@/types/consul").ConsulEvent> {
|
||
return post("/api/consul/event/fire", { connectionId, request });
|
||
}
|
||
export async function consulCoordinateNodes(connectionId: string): Promise<import("@/types/consul").ConsulCoordinate[]> {
|
||
return post("/api/consul/coordinate/nodes", { connectionId });
|
||
}
|
||
export async function consulOperatorRead(connectionId: string, kind: import("@/types/consul").ConsulOperatorReadKind): Promise<import("@/types/consul").ConsulOperatorDocument> {
|
||
return post("/api/consul/operator/read", { connectionId, kind });
|
||
}
|
||
export async function consulSnapshotGenerate(connectionId: string): Promise<import("@/types/consul").ConsulSnapshot> {
|
||
return post("/api/consul/operator/snapshot/generate", { connectionId });
|
||
}
|
||
export async function consulSnapshotRestore(connectionId: string, request: import("@/types/consul").ConsulSnapshotRestoreRequest): Promise<void> {
|
||
await post("/api/consul/operator/snapshot/restore", { connectionId, request });
|
||
}
|
||
export async function consulAutopilotUpdate(connectionId: string, update: import("@/types/consul").ConsulAutopilotUpdate, confirmation: string): Promise<void> {
|
||
await post("/api/consul/operator/autopilot/update", { connectionId, update, confirmation });
|
||
}
|
||
export async function consulRaftTransfer(connectionId: string, request: import("@/types/consul").ConsulRaftWriteRequest): Promise<void> {
|
||
await post("/api/consul/operator/raft/transfer", { connectionId, request });
|
||
}
|
||
export async function consulRaftRemove(connectionId: string, request: import("@/types/consul").ConsulRaftWriteRequest): Promise<void> {
|
||
await post("/api/consul/operator/raft/remove", { connectionId, request });
|
||
}
|
||
export async function consulKeyringWrite(connectionId: string, request: import("@/types/consul").ConsulKeyringWriteRequest): Promise<void> {
|
||
await post("/api/consul/operator/keyring/write", { connectionId, request });
|
||
}
|
||
export async function consulLicenseWrite(connectionId: string, request: import("@/types/consul").ConsulLicenseWriteRequest): Promise<void> {
|
||
await post("/api/consul/operator/license/write", { connectionId, request });
|
||
}
|
||
|
||
export async function consulStatusLeader(connectionId: string): Promise<string> {
|
||
return post("/api/consul/status/leader", { connectionId });
|
||
}
|
||
export async function consulStatusPeers(connectionId: string): Promise<string[]> {
|
||
return post("/api/consul/status/peers", { connectionId });
|
||
}
|
||
export async function consulAgentSelf(connectionId: string): Promise<import("@/types/consul").ConsulAgentIdentity> {
|
||
return post("/api/consul/agent/self", { connectionId });
|
||
}
|
||
export async function consulAgentMembers(connectionId: string, wan = false, segment?: string | null): Promise<import("@/types/consul").ConsulAgentMember[]> {
|
||
return post("/api/consul/agent/members", { connectionId, wan, segment: segment ?? null });
|
||
}
|
||
export async function consulAgentMetrics(connectionId: string): Promise<unknown> {
|
||
return post("/api/consul/agent/metrics", { connectionId });
|
||
}
|
||
export async function consulCatalogDatacenters(connectionId: string): Promise<string[]> {
|
||
return post("/api/consul/catalog/datacenters", { connectionId });
|
||
}
|
||
export async function consulCatalogNodes(connectionId: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulCatalogNode[]>> {
|
||
return post("/api/consul/catalog/nodes", { connectionId, options });
|
||
}
|
||
export async function consulCatalogServices(connectionId: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<Record<string, string[]>>> {
|
||
return post("/api/consul/catalog/services", { connectionId, options });
|
||
}
|
||
export async function consulCatalogServiceNodes(connectionId: string, service: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulCatalogServiceNode[]>> {
|
||
return post("/api/consul/catalog/service-nodes", { connectionId, name: service, options });
|
||
}
|
||
export async function consulCatalogNodeServices(connectionId: string, node: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulNodeServices>> {
|
||
return post("/api/consul/catalog/node-services", { connectionId, name: node, options });
|
||
}
|
||
export async function consulHealthNode(connectionId: string, node: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulHealthCheck[]>> {
|
||
return post("/api/consul/health/node", { connectionId, name: node, options });
|
||
}
|
||
export async function consulHealthChecks(connectionId: string, service: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulHealthCheck[]>> {
|
||
return post("/api/consul/health/checks", { connectionId, name: service, options });
|
||
}
|
||
export async function consulHealthService(connectionId: string, service: string, passing: boolean | null = null, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulServiceInstance[]>> {
|
||
return post("/api/consul/health/service", { connectionId, name: service, passing, options });
|
||
}
|
||
export async function consulHealthState(connectionId: string, healthState: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulHealthCheck[]>> {
|
||
return post("/api/consul/health/state", { connectionId, name: healthState, options });
|
||
}
|
||
export async function consulAgentServices(connectionId: string): Promise<Record<string, import("@/types/consul").ConsulAgentService>> {
|
||
return post("/api/consul/agent/services", { connectionId });
|
||
}
|
||
export async function consulAgentService(connectionId: string, id: string): Promise<import("@/types/consul").ConsulAgentService> {
|
||
return post("/api/consul/agent/service", { connectionId, id });
|
||
}
|
||
export async function consulAgentChecks(connectionId: string): Promise<Record<string, import("@/types/consul").ConsulHealthCheck>> {
|
||
return post("/api/consul/agent/checks", { connectionId });
|
||
}
|
||
export async function consulAgentRegisterService(connectionId: string, registration: import("@/types/consul").ConsulAgentServiceRegistration): Promise<import("@/types/consul").ConsulAgentWriteResult> {
|
||
return post("/api/consul/agent/service/register", { connectionId, registration });
|
||
}
|
||
export async function consulAgentDeregisterService(connectionId: string, id: string): Promise<import("@/types/consul").ConsulAgentWriteResult> {
|
||
return post("/api/consul/agent/service/deregister", { connectionId, id });
|
||
}
|
||
export async function consulAgentServiceMaintenance(connectionId: string, id: string, enable: boolean, reason?: string | null): Promise<import("@/types/consul").ConsulAgentWriteResult> {
|
||
return post("/api/consul/agent/service/maintenance", { connectionId, id, enable, reason: reason ?? null });
|
||
}
|
||
export async function consulAgentRegisterCheck(connectionId: string, registration: import("@/types/consul").ConsulAgentCheckRegistration): Promise<import("@/types/consul").ConsulAgentWriteResult> {
|
||
return post("/api/consul/agent/check/register", { connectionId, registration });
|
||
}
|
||
export async function consulAgentDeregisterCheck(connectionId: string, id: string): Promise<import("@/types/consul").ConsulAgentWriteResult> {
|
||
return post("/api/consul/agent/check/deregister", { connectionId, id });
|
||
}
|
||
export async function consulAgentUpdateTtl(connectionId: string, id: string, status: import("@/types/consul").ConsulCheckStatus, output?: string | null): Promise<import("@/types/consul").ConsulAgentWriteResult> {
|
||
return post("/api/consul/agent/check/ttl", { connectionId, id, status, output: output ?? null });
|
||
}
|
||
export async function consulSessions(connectionId: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulSession[]>> {
|
||
return post("/api/consul/sessions", { connectionId, options });
|
||
}
|
||
export async function consulNodeSessions(connectionId: string, node: string, options: import("@/types/consul").ConsulReadOptions = {}): Promise<import("@/types/consul").ConsulListResponse<import("@/types/consul").ConsulSession[]>> {
|
||
return post("/api/consul/sessions/node", { connectionId, name: node, options });
|
||
}
|
||
export async function consulSession(connectionId: string, id: string): Promise<import("@/types/consul").ConsulSession | null> {
|
||
return post("/api/consul/session", { connectionId, id });
|
||
}
|
||
export async function consulSessionKeys(connectionId: string, id: string): Promise<import("@/types/consul").ConsulSessionKeysResponse> {
|
||
return post("/api/consul/session/keys", { connectionId, id });
|
||
}
|
||
export async function consulSessionDestroyImpact(connectionId: string, id: string): Promise<import("@/types/consul").ConsulSessionDestroyImpact> {
|
||
return post("/api/consul/session/destroy-impact", { connectionId, id });
|
||
}
|
||
export async function consulCreateSession(connectionId: string, request: import("@/types/consul").ConsulSessionCreateRequest): Promise<import("@/types/consul").ConsulSession> {
|
||
return post("/api/consul/session/create", { connectionId, request });
|
||
}
|
||
export async function consulRenewSession(connectionId: string, id: string): Promise<import("@/types/consul").ConsulSession> {
|
||
return post("/api/consul/session/renew", { connectionId, id });
|
||
}
|
||
export async function consulDestroySession(connectionId: string, request: import("@/types/consul").ConsulSessionDestroyRequest): Promise<boolean> {
|
||
return post("/api/consul/session/destroy", { connectionId, request });
|
||
}
|
||
export async function consulAcquireLock(connectionId: string, request: import("@/types/consul").ConsulLockRequest): Promise<import("@/types/consul").ConsulLockResponse> {
|
||
return post("/api/consul/lock/acquire", { connectionId, request });
|
||
}
|
||
export async function consulReleaseLock(connectionId: string, key: string, session: string): Promise<import("@/types/consul").ConsulLockResponse> {
|
||
return post("/api/consul/lock/release", { connectionId, key, session });
|
||
}
|
||
|
||
export async function consulAclList(connectionId: string, kind: import("@/types/consul").ConsulAclKind): Promise<import("@/types/consul").ConsulAclList> {
|
||
return post("/api/consul/acl/list", { connectionId, kind });
|
||
}
|
||
export async function consulAclTokenSelf(connectionId: string): Promise<import("@/types/consul").ConsulAclToken> {
|
||
return post("/api/consul/acl/token/self", { connectionId });
|
||
}
|
||
export async function consulAclTokenClone(connectionId: string, accessorId: string, description: string): Promise<import("@/types/consul").ConsulAclToken> {
|
||
return post("/api/consul/acl/token/clone", { connectionId, id: accessorId, description });
|
||
}
|
||
export async function consulAclGet(connectionId: string, kind: import("@/types/consul").ConsulAclKind, id: string): Promise<import("@/types/consul").ConsulAclItem> {
|
||
return post("/api/consul/acl/get", { connectionId, kind, id });
|
||
}
|
||
export async function consulAclApply(connectionId: string, id: string | null, value: import("@/types/consul").ConsulAclWrite): Promise<import("@/types/consul").ConsulAclItem> {
|
||
return post("/api/consul/acl/apply", { connectionId, id, value });
|
||
}
|
||
export async function consulAclReferences(connectionId: string, kind: import("@/types/consul").ConsulAclKind, id: string): Promise<import("@/types/consul").ConsulAclReferences> {
|
||
return post("/api/consul/acl/references", { connectionId, kind, id });
|
||
}
|
||
export async function consulAclDelete(connectionId: string, kind: import("@/types/consul").ConsulAclKind, id: string): Promise<import("@/types/consul").ConsulAclReferences> {
|
||
return post("/api/consul/acl/delete", { connectionId, kind, id });
|
||
}
|
||
export async function consulEnterpriseList(connectionId: string, kind: import("@/types/consul").ConsulEnterpriseKind): Promise<import("@/types/consul").ConsulEnterpriseList> {
|
||
return post("/api/consul/enterprise/list", { connectionId, kind });
|
||
}
|
||
export async function consulEnterpriseGet(connectionId: string, kind: import("@/types/consul").ConsulEnterpriseKind, name: string): Promise<import("@/types/consul").ConsulEnterpriseItem> {
|
||
return post("/api/consul/enterprise/get", { connectionId, kind, name });
|
||
}
|
||
export async function consulEnterpriseApply(connectionId: string, existingName: string | null, item: import("@/types/consul").ConsulEnterpriseWrite): Promise<import("@/types/consul").ConsulEnterpriseItem> {
|
||
return post("/api/consul/enterprise/apply", { connectionId, existingName, item });
|
||
}
|
||
export async function consulEnterpriseImpact(connectionId: string, kind: import("@/types/consul").ConsulEnterpriseKind, name: string): Promise<import("@/types/consul").ConsulScopeImpact> {
|
||
return post("/api/consul/enterprise/impact", { connectionId, kind, name });
|
||
}
|
||
export async function consulEnterpriseDelete(connectionId: string, kind: import("@/types/consul").ConsulEnterpriseKind, name: string): Promise<import("@/types/consul").ConsulScopeImpact> {
|
||
return post("/api/consul/enterprise/delete", { connectionId, kind, name });
|
||
}
|
||
export async function consulMeshConfigList(connectionId: string, kind: string): Promise<import("@/types/consul").ConsulConfigEntry[]> {
|
||
return post("/api/consul/mesh/config/list", { connectionId, kind });
|
||
}
|
||
export async function consulMeshConfigGet(connectionId: string, kind: string, name: string): Promise<import("@/types/consul").ConsulConfigEntry> {
|
||
return post("/api/consul/mesh/config/get", { connectionId, kind, name });
|
||
}
|
||
export async function consulMeshConfigApply(connectionId: string, request: import("@/types/consul").ConsulConfigEntryApply): Promise<import("@/types/consul").ConsulConfigEntry> {
|
||
return post("/api/consul/mesh/config/apply", { connectionId, request });
|
||
}
|
||
export async function consulMeshConfigDelete(connectionId: string, kind: string, name: string, expectedModifyIndex: number): Promise<boolean> {
|
||
return post("/api/consul/mesh/config/delete", { connectionId, kind, name, expectedModifyIndex });
|
||
}
|
||
export async function consulMeshIntentionsList(connectionId: string): Promise<import("@/types/consul").ConsulIntention[]> {
|
||
return post("/api/consul/mesh/intentions/list", { connectionId });
|
||
}
|
||
export async function consulMeshIntentionGet(connectionId: string, id: string): Promise<import("@/types/consul").ConsulIntention> {
|
||
return post("/api/consul/mesh/intentions/get", { connectionId, id });
|
||
}
|
||
export async function consulMeshIntentionGetExact(connectionId: string, request: import("@/types/consul").ConsulIntentionExactRequest): Promise<import("@/types/consul").ConsulIntention> {
|
||
return post("/api/consul/mesh/intentions/get-exact", { connectionId, exactRequest: request });
|
||
}
|
||
export async function consulMeshIntentionUpsert(connectionId: string, item: import("@/types/consul").ConsulIntention): Promise<import("@/types/consul").ConsulIntention> {
|
||
return post("/api/consul/mesh/intentions/upsert", { connectionId, intention: item });
|
||
}
|
||
export async function consulMeshIntentionDelete(connectionId: string, id: string): Promise<boolean> {
|
||
return post("/api/consul/mesh/intentions/delete", { connectionId, id });
|
||
}
|
||
export async function consulMeshIntentionDeleteExact(connectionId: string, request: import("@/types/consul").ConsulIntentionExactRequest): Promise<boolean> {
|
||
return post("/api/consul/mesh/intentions/delete-exact", { connectionId, exactRequest: request });
|
||
}
|
||
export async function consulMeshIntentionMatch(connectionId: string, request: import("@/types/consul").ConsulIntentionMatchRequest): Promise<import("@/types/consul").ConsulIntention[]> {
|
||
return post("/api/consul/mesh/intentions/match", { connectionId, matchRequest: request });
|
||
}
|
||
export async function consulMeshIntentionCheck(connectionId: string, request: import("@/types/consul").ConsulIntentionCheckRequest): Promise<import("@/types/consul").ConsulIntentionCheckResponse> {
|
||
return post("/api/consul/mesh/intentions/check", { connectionId, checkRequest: request });
|
||
}
|
||
export async function consulMeshDiscoveryChain(connectionId: string, service: string): Promise<import("@/types/consul").ConsulDiscoveryChain> {
|
||
return post("/api/consul/mesh/discovery-chain", { connectionId, service });
|
||
}
|
||
export async function consulMeshPeeringList(connectionId: string): Promise<import("@/types/consul").ConsulPeering[]> {
|
||
return post("/api/consul/mesh/peerings/list", { connectionId });
|
||
}
|
||
export async function consulMeshPeeringGet(connectionId: string, name: string): Promise<import("@/types/consul").ConsulPeering> {
|
||
return post("/api/consul/mesh/peerings/get", { connectionId, name });
|
||
}
|
||
export async function consulMeshPeeringGenerateToken(connectionId: string, request: import("@/types/consul").ConsulPeeringGenerateRequest): Promise<import("@/types/consul").ConsulPeeringToken> {
|
||
return post("/api/consul/mesh/peerings/generate-token", { connectionId, generateRequest: request });
|
||
}
|
||
export async function consulMeshPeeringEstablish(connectionId: string, request: import("@/types/consul").ConsulPeeringEstablishRequest): Promise<import("@/types/consul").ConsulPeering> {
|
||
return post("/api/consul/mesh/peerings/establish", { connectionId, establishRequest: request });
|
||
}
|
||
export async function consulMeshPeeringDelete(connectionId: string, name: string): Promise<boolean> {
|
||
return post("/api/consul/mesh/peerings/delete", { connectionId, name });
|
||
}
|
||
export async function consulMeshExportedServicesList(connectionId: string): Promise<import("@/types/consul").ConsulExportedService[]> {
|
||
return post("/api/consul/mesh/exported-services/list", { connectionId });
|
||
}
|
||
export async function consulMeshExportedServicesApply(connectionId: string, name: string, expectedModifyIndex: number, raw: Record<string, unknown>): Promise<import("@/types/consul").ConsulConfigEntry> {
|
||
return post("/api/consul/mesh/exported-services/apply", { connectionId, name, expectedModifyIndex, raw });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Nacos
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function nacosTestConnection(connectionId: string, forceRefresh = false): Promise<NacosConnectionInfo> {
|
||
return post("/api/nacos/test-connection", { connectionId, forceRefresh });
|
||
}
|
||
|
||
export async function nacosListNamespaces(connectionId: string): Promise<NacosNamespaceInfo[]> {
|
||
return post("/api/nacos/namespaces/list", { connectionId });
|
||
}
|
||
|
||
export async function nacosSidebarSnapshot(connectionId: string): Promise<NacosNamespaceSidebarSnapshot> {
|
||
return post("/api/nacos/sidebar/snapshot", { connectionId });
|
||
}
|
||
|
||
export async function nacosCreateNamespace(connectionId: string, req: NacosNamespaceCreate): Promise<void> {
|
||
return post("/api/nacos/namespaces/create", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosUpdateNamespace(connectionId: string, req: NacosNamespaceUpdate): Promise<void> {
|
||
return post("/api/nacos/namespaces/update", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosDeleteNamespace(connectionId: string, namespaceId: string): Promise<void> {
|
||
return post("/api/nacos/namespaces/delete", { connectionId, namespaceId });
|
||
}
|
||
|
||
export async function nacosListConfigs(connectionId: string, query: NacosConfigQuery): Promise<NacosConfigList> {
|
||
return post("/api/nacos/configs/list", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosGetConfig(connectionId: string, key: NacosConfigKey): Promise<NacosConfigItem> {
|
||
return post("/api/nacos/configs/get", { connectionId, key });
|
||
}
|
||
|
||
export async function nacosPublishConfig(connectionId: string, req: NacosConfigUpsert): Promise<void> {
|
||
return post("/api/nacos/configs/publish", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosDeleteConfig(connectionId: string, key: NacosConfigKey): Promise<void> {
|
||
return post("/api/nacos/configs/delete", { connectionId, key });
|
||
}
|
||
|
||
export async function nacosSearchConfigContent(connectionId: string, req: NacosContentSearchRequest, onProgress?: (progress: NacosSearchProgress) => void): Promise<NacosContentSearchResult> {
|
||
const response = await fetch(apiUrl("/api/nacos/configs/search"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ connectionId, req }),
|
||
});
|
||
if (!response.ok) throw await backendResponseError(response);
|
||
if (!response.body) throw new Error("Nacos content search did not return a response stream");
|
||
|
||
const reader = response.body.getReader();
|
||
const decoder = new TextDecoder();
|
||
let buffer = "";
|
||
let result: NacosContentSearchResult | null = null;
|
||
|
||
const consumeLine = (line: string) => {
|
||
if (!line.startsWith("data:")) return;
|
||
const data = line.slice(5).trim();
|
||
if (!data) return;
|
||
const event = JSON.parse(data) as { type: "progress"; progress: NacosSearchProgress } | { type: "result"; result: NacosContentSearchResult } | { type: "error"; error: string };
|
||
if (event.type === "progress") onProgress?.(event.progress);
|
||
else if (event.type === "result") result = event.result;
|
||
else throw new Error(event.error);
|
||
};
|
||
|
||
try {
|
||
while (true) {
|
||
const { done, value } = await reader.read();
|
||
if (done) break;
|
||
buffer += decoder.decode(value, { stream: true });
|
||
const lines = buffer.split("\n");
|
||
buffer = lines.pop() || "";
|
||
for (const line of lines) consumeLine(line);
|
||
}
|
||
buffer += decoder.decode();
|
||
if (buffer) consumeLine(buffer);
|
||
if (!result) throw new Error("Nacos content search stream ended without a final result");
|
||
return result;
|
||
} finally {
|
||
await reader.cancel().catch(() => {});
|
||
}
|
||
}
|
||
|
||
export async function nacosCancelConfigContentSearch(operationId: string): Promise<boolean> {
|
||
const result = await post<{ cancelled: boolean }>("/api/nacos/configs/search/cancel", { operationId });
|
||
return result.cancelled;
|
||
}
|
||
|
||
export async function nacosExportConfigs(connectionId: string, selector: NacosConfigSelector, _destination: string, fileName = "nacos-configs.zip"): Promise<void> {
|
||
const response = await fetch(apiUrl("/api/nacos/configs/export"), {
|
||
method: "POST",
|
||
headers: { "content-type": "application/json" },
|
||
body: JSON.stringify({ connectionId, selector, fileName }),
|
||
});
|
||
if (!response.ok) throw await backendResponseError(response);
|
||
const blob = await response.blob();
|
||
const url = URL.createObjectURL(blob);
|
||
const anchor = document.createElement("a");
|
||
anchor.href = url;
|
||
anchor.download = fileName;
|
||
anchor.click();
|
||
setTimeout(() => URL.revokeObjectURL(url), 0);
|
||
}
|
||
|
||
export async function nacosPreviewConfigImport(connectionId: string, targetNamespace: string, archivePath: string | File): Promise<NacosBatchPreview> {
|
||
if (!(archivePath instanceof File)) throw new Error("Nacos ZIP import in web mode requires a File object");
|
||
const formData = new FormData();
|
||
formData.append("connectionId", connectionId);
|
||
formData.append("targetNamespace", targetNamespace);
|
||
formData.append("file", archivePath, archivePath.name);
|
||
const response = await fetch(apiUrl("/api/nacos/configs/import/preview"), {
|
||
method: "POST",
|
||
body: formData,
|
||
});
|
||
if (!response.ok) throw await backendResponseError(response);
|
||
return response.json();
|
||
}
|
||
|
||
export async function nacosApplyConfigImport(connectionId: string, operationId: string, targetNamespace: string, _archivePath: string | File, planHash: string, conflictPolicy: NacosConflictPolicy, archiveToken?: string): Promise<NacosBatchReport> {
|
||
if (!archiveToken) throw new Error("The Nacos import preview token is missing or expired");
|
||
return post("/api/nacos/configs/import/apply", {
|
||
connectionId,
|
||
operationId,
|
||
targetNamespace,
|
||
archiveToken,
|
||
planHash,
|
||
conflictPolicy,
|
||
});
|
||
}
|
||
|
||
export async function nacosPreviewConfigTransfer(req: NacosConfigTransferRequest): Promise<NacosBatchPreview> {
|
||
return post("/api/nacos/configs/copy/preview", { req });
|
||
}
|
||
|
||
export async function nacosApplyConfigTransfer(req: NacosConfigTransferRequest, planHash: string): Promise<NacosBatchReport> {
|
||
return post("/api/nacos/configs/copy/apply", { req, planHash });
|
||
}
|
||
|
||
export async function nacosListConfigHistory(connectionId: string, query: NacosConfigHistoryQuery): Promise<NacosConfigHistoryList> {
|
||
return post("/api/nacos/configs/history/list", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosGetConfigHistory(connectionId: string, key: NacosConfigHistoryKey): Promise<NacosConfigItem> {
|
||
return post("/api/nacos/configs/history/get", { connectionId, key });
|
||
}
|
||
|
||
export async function nacosRollbackConfig(connectionId: string, req: NacosConfigRollbackRequest): Promise<void> {
|
||
return post("/api/nacos/configs/history/rollback", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosGetRNacosConsoleCaptcha(connectionId: string): Promise<NacosRNacosConsoleCaptcha> {
|
||
return post("/api/nacos/rnacos-console/captcha", { connectionId });
|
||
}
|
||
|
||
export async function nacosLoginRNacosConsole(connectionId: string, captcha?: string): Promise<void> {
|
||
return post("/api/nacos/rnacos-console/login", { connectionId, captcha });
|
||
}
|
||
|
||
export async function nacosListUsers(connectionId: string, query: NacosUserQuery): Promise<NacosUserList> {
|
||
return post("/api/nacos/users/list", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosCreateUser(connectionId: string, req: NacosUserCreate): Promise<void> {
|
||
return post("/api/nacos/users/create", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosUpdateUser(connectionId: string, req: NacosUserUpdate): Promise<void> {
|
||
return post("/api/nacos/users/update", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosDeleteUser(connectionId: string, username: string): Promise<void> {
|
||
return post("/api/nacos/users/delete", { connectionId, username });
|
||
}
|
||
|
||
export async function nacosListRoleBindings(connectionId: string, query: NacosRoleQuery): Promise<NacosRoleList> {
|
||
return post("/api/nacos/roles/list", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosAssignRole(connectionId: string, binding: NacosRoleBinding): Promise<void> {
|
||
return post("/api/nacos/roles/assign", { connectionId, binding });
|
||
}
|
||
|
||
export async function nacosRemoveRole(connectionId: string, binding: NacosRoleBinding): Promise<void> {
|
||
return post("/api/nacos/roles/remove", { connectionId, binding });
|
||
}
|
||
|
||
export async function nacosAccessSnapshot(connectionId: string): Promise<NacosAccessControlSnapshot> {
|
||
return post("/api/nacos/access/snapshot", { connectionId });
|
||
}
|
||
|
||
export async function nacosStartAccessOperation(connectionId: string, req: NacosAccessOperationRequest): Promise<NacosAccessOperationResult> {
|
||
return post("/api/nacos/access/operations/start", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosGetAccessOperation(connectionId: string, operationId: string): Promise<NacosAccessOperationResult> {
|
||
return post("/api/nacos/access/operations/get", { connectionId, operationId });
|
||
}
|
||
|
||
export async function nacosRetryAccessOperation(connectionId: string, retry: NacosAccessOperationRetry): Promise<NacosAccessOperationResult> {
|
||
return post("/api/nacos/access/operations/retry", { connectionId, retry });
|
||
}
|
||
|
||
export async function nacosUndoAccessOperation(connectionId: string, operationId: string): Promise<NacosAccessOperationResult> {
|
||
return post("/api/nacos/access/operations/undo", { connectionId, operationId });
|
||
}
|
||
|
||
export async function nacosListServices(connectionId: string, query: NacosServiceQuery): Promise<NacosServiceList> {
|
||
return post("/api/nacos/services/list", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosGetService(connectionId: string, query: NacosServiceQuery): Promise<NacosServiceDetail> {
|
||
return post("/api/nacos/services/get", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosCreateService(connectionId: string, req: NacosServiceUpsert): Promise<void> {
|
||
return post("/api/nacos/services/create", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosUpdateService(connectionId: string, req: NacosServiceUpsert): Promise<void> {
|
||
return post("/api/nacos/services/update", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosDeleteService(connectionId: string, query: NacosServiceQuery): Promise<void> {
|
||
return post("/api/nacos/services/delete", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosListInstances(connectionId: string, query: NacosInstanceQuery): Promise<NacosInstanceInfo[]> {
|
||
return post("/api/nacos/instances/list", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosUpdateInstance(connectionId: string, req: NacosInstanceUpdateRequest): Promise<void> {
|
||
return post("/api/nacos/instances/update", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosRegisterInstance(connectionId: string, req: NacosInstanceRegistration): Promise<void> {
|
||
return post("/api/nacos/instances/register", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosDeregisterInstance(connectionId: string, req: NacosInstanceRef): Promise<void> {
|
||
return post("/api/nacos/instances/deregister", { connectionId, req });
|
||
}
|
||
|
||
export async function nacosGetDashboard(connectionId: string, query: NacosDashboardQuery): Promise<NacosDashboardSnapshot> {
|
||
return post("/api/nacos/dashboard", { connectionId, query });
|
||
}
|
||
|
||
export async function nacosRawRequest(connectionId: string, req: NacosRawRequest): Promise<NacosRawResponse> {
|
||
return post("/api/nacos/raw", { connectionId, req });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// HBase
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function hbaseGetTableSchema(connectionId: string, namespace: string, table: string): Promise<import("@/types/hbase").HBaseTableSchema> {
|
||
return post("/api/hbase/table-schema", { connectionId, namespace, table });
|
||
}
|
||
|
||
export async function hbaseScanRows(connectionId: string, namespace: string, table: string, rowKeyPrefix: string | undefined, limit: number): Promise<import("@/types/hbase").HBaseScanResult> {
|
||
return post("/api/hbase/scan-rows", {
|
||
connectionId,
|
||
namespace,
|
||
table,
|
||
rowKeyPrefix,
|
||
limit,
|
||
});
|
||
}
|
||
|
||
export async function hbaseGetRow(connectionId: string, namespace: string, table: string, rowKey: string, rowKeyEncoding?: import("@/types/hbase").HBaseValueEncoding): Promise<import("@/types/hbase").HBaseRow | null> {
|
||
return post("/api/hbase/get-row", {
|
||
connectionId,
|
||
namespace,
|
||
table,
|
||
rowKey,
|
||
rowKeyEncoding,
|
||
});
|
||
}
|
||
|
||
export async function hbasePutRow(connectionId: string, namespace: string, table: string, input: import("@/types/hbase").HBasePutRowInput): Promise<void> {
|
||
return post("/api/hbase/put-row", { connectionId, namespace, table, input });
|
||
}
|
||
|
||
export async function hbaseDeleteRow(connectionId: string, namespace: string, table: string, rowKey: string, rowKeyEncoding?: import("@/types/hbase").HBaseValueEncoding): Promise<void> {
|
||
return post("/api/hbase/delete-row", {
|
||
connectionId,
|
||
namespace,
|
||
table,
|
||
rowKey,
|
||
rowKeyEncoding,
|
||
});
|
||
}
|
||
|
||
export async function hbaseCreateTable(connectionId: string, namespace: string, table: string, columnFamilies: string[]): Promise<void> {
|
||
return post("/api/hbase/create-table", {
|
||
connectionId,
|
||
namespace,
|
||
table,
|
||
columnFamilies,
|
||
});
|
||
}
|
||
|
||
export async function hbaseDeleteTable(connectionId: string, namespace: string, table: string): Promise<void> {
|
||
return post("/api/hbase/delete-table", { connectionId, namespace, table });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// MongoDB
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function documentListDatabases(connectionId: string): Promise<string[]> {
|
||
return post("/api/document-store/list-databases", { connectionId });
|
||
}
|
||
|
||
export async function mongoListDatabases(connectionId: string): Promise<string[]> {
|
||
return documentListDatabases(connectionId);
|
||
}
|
||
|
||
export async function documentListCollections(connectionId: string, database: string): Promise<CollectionInfo[]> {
|
||
return post("/api/document-store/list-collections", {
|
||
connectionId,
|
||
database,
|
||
});
|
||
}
|
||
|
||
export async function mongoListCollections(connectionId: string, database: string): Promise<CollectionInfo[]> {
|
||
return documentListCollections(connectionId, database);
|
||
}
|
||
|
||
export async function mongoCreateDatabase(connectionId: string, database: string): Promise<void> {
|
||
await post("/api/mongo/create-database", { connectionId, database });
|
||
}
|
||
|
||
export async function mongoDropDatabase(connectionId: string, database: string): Promise<void> {
|
||
await post("/api/mongo/drop-database", { connectionId, database });
|
||
}
|
||
|
||
export async function mongoDropCollection(connectionId: string, database: string, collection: string): Promise<void> {
|
||
await post("/api/mongo/drop-collection", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
});
|
||
}
|
||
|
||
export async function mongoRenameCollection(connectionId: string, database: string, collection: string, newName: string): Promise<void> {
|
||
await post("/api/mongo/rename-collection", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
newName,
|
||
});
|
||
}
|
||
|
||
export async function mongoCloneCollection(connectionId: string, database: string, sourceCollection: string, targetCollection: string): Promise<MongoCloneCollectionResult> {
|
||
return post("/api/mongo/clone-collection", {
|
||
connectionId,
|
||
database,
|
||
sourceCollection,
|
||
targetCollection,
|
||
});
|
||
}
|
||
|
||
export async function elasticsearchListIndices(connectionId: string): Promise<string[]> {
|
||
const collections = await documentListCollections(connectionId, "default");
|
||
return [...new Set(collections.flatMap((collection) => [collection.name, ...(collection.aliases ?? [])].filter((name) => name.trim())))];
|
||
}
|
||
|
||
/** Lists every Meilisearch index visible to the current connection credentials. */
|
||
export async function meilisearchListIndexes(connectionId: string): Promise<string[]> {
|
||
const collections = await documentListCollections(connectionId, "default");
|
||
return collections.map((collection) => collection.name);
|
||
}
|
||
|
||
export async function vectorListCollections(connectionId: string, database?: string): Promise<CollectionInfo[]> {
|
||
return documentListCollections(connectionId, database || "default");
|
||
}
|
||
|
||
export async function vectorGetCollectionDetail(connectionId: string, database: string, collection: string): Promise<CollectionInfo> {
|
||
return post("/api/vector/collection-detail", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
});
|
||
}
|
||
|
||
export async function vectorDropDatabase(connectionId: string, database: string): Promise<void> {
|
||
await post("/api/vector/drop-database", { connectionId, database });
|
||
}
|
||
|
||
export async function vectorDropCollection(connectionId: string, database: string, collection: string): Promise<void> {
|
||
await post("/api/vector/drop-collection", { connectionId, database, collection });
|
||
}
|
||
|
||
export async function vectorRenameCollection(connectionId: string, database: string, collection: string, newName: string): Promise<void> {
|
||
await post("/api/vector/rename-collection", { connectionId, database, collection, newName });
|
||
}
|
||
|
||
export async function mongoFindDocuments(connectionId: string, database: string, collection: string, skip: number, limit: number, filter?: string, projection?: string, sort?: string, collation?: string, executionId?: string, includeTotal?: boolean): Promise<MongoDocumentResult> {
|
||
return documentFindDocuments(connectionId, database, collection, skip, limit, filter, projection, sort, collation, executionId, undefined, undefined, includeTotal);
|
||
}
|
||
|
||
export async function mongoParseShellCommand(source: string): Promise<MongoCommand> {
|
||
const raw = await post<Record<string, unknown>>("/api/mongo/parse-shell-command", { source });
|
||
return normalizeRustMongoCommand(raw);
|
||
}
|
||
|
||
export async function mongoFindOne(connectionId: string, database: string, collection: string, filter?: string, projection?: string, options?: string, executionId?: string): Promise<MongoDocumentResult> {
|
||
return post("/api/mongo/find-one", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
filter,
|
||
projection,
|
||
options,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function documentFindDocuments(
|
||
connectionId: string,
|
||
database: string,
|
||
collection: string,
|
||
skip: number,
|
||
limit: number,
|
||
filter?: string,
|
||
projection?: string,
|
||
sort?: string,
|
||
collation?: string,
|
||
executionId?: string,
|
||
cursor?: string,
|
||
cursorPagination?: boolean,
|
||
includeTotal?: boolean,
|
||
): Promise<DocumentQueryResult> {
|
||
return post("/api/document-store/find-documents", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
skip,
|
||
limit,
|
||
filter,
|
||
projection,
|
||
sort,
|
||
collation,
|
||
cursor,
|
||
cursorPagination,
|
||
includeTotal,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function documentCountDocuments(connectionId: string, collection: string, filter?: string, executionId?: string): Promise<number> {
|
||
return post("/api/document-store/count-documents", {
|
||
connectionId,
|
||
collection,
|
||
filter,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function dynamodbDescribeTable(connectionId: string, table: string): Promise<DynamoDbTableDescription> {
|
||
return post("/api/document-store/dynamodb-describe-table", { connectionId, table });
|
||
}
|
||
|
||
export async function elasticsearchCountDocuments(connectionId: string, index: string, filter?: string, executionId?: string): Promise<number> {
|
||
return post("/api/document-store/elasticsearch-count-documents", {
|
||
connectionId,
|
||
index,
|
||
filter,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function elasticsearchGetIndexMetadata(connectionId: string, index: string, kind: ElasticsearchIndexMetadataKind): Promise<Record<string, unknown>> {
|
||
return post("/api/document-store/elasticsearch/index-metadata", {
|
||
connectionId,
|
||
index,
|
||
kind,
|
||
});
|
||
}
|
||
|
||
export async function elasticsearchDeleteAllDocuments(connectionId: string, index: string): Promise<ElasticsearchDeleteByQueryResult> {
|
||
return post("/api/document-store/elasticsearch/documents/delete-all", {
|
||
connectionId,
|
||
index,
|
||
});
|
||
}
|
||
|
||
export async function mongoCountDocuments(connectionId: string, database: string, collection: string, filter?: string, mode?: "accurate" | "legacy", executionId?: string): Promise<number> {
|
||
return post("/api/mongo/count-documents", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
filter,
|
||
mode,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function documentListGridFsFiles(connectionId: string, database: string, bucket: string, filter?: string, sort?: string): Promise<MongoGridFsFileInfo[]> {
|
||
return post("/api/document-store/list-gridfs-files", {
|
||
connectionId,
|
||
database,
|
||
bucket,
|
||
filter,
|
||
sort,
|
||
});
|
||
}
|
||
|
||
export async function documentListGridFsBuckets(connectionId: string, database: string, filter?: string, sort?: string): Promise<MongoGridFsBucketInfo[]> {
|
||
return post("/api/document-store/list-gridfs-buckets", {
|
||
connectionId,
|
||
database,
|
||
filter,
|
||
sort,
|
||
});
|
||
}
|
||
|
||
export async function documentCreateGridFsBucket(connectionId: string, database: string, bucket: string): Promise<void> {
|
||
return post("/api/document-store/create-gridfs-bucket", {
|
||
connectionId,
|
||
database,
|
||
bucket,
|
||
});
|
||
}
|
||
|
||
export async function documentDeleteGridFsBucket(connectionId: string, database: string, bucket: string): Promise<void> {
|
||
return post("/api/document-store/delete-gridfs-bucket", {
|
||
connectionId,
|
||
database,
|
||
bucket,
|
||
});
|
||
}
|
||
|
||
export async function documentDownloadGridFsFile(connectionId: string, database: string, bucket: string, fileId: string): Promise<Uint8Array> {
|
||
const res = await fetch(apiUrl("/api/document-store/download-gridfs-file"), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ connectionId, database, bucket, fileId }),
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
const data = (await res.json()) as number[];
|
||
return new Uint8Array(data);
|
||
}
|
||
|
||
export async function documentUploadGridFsFile(connectionId: string, database: string, bucket: string, fileName: string, data: Uint8Array, contentType?: string): Promise<string> {
|
||
const body = new FormData();
|
||
body.append("connectionId", connectionId);
|
||
body.append("database", database);
|
||
body.append("bucket", bucket);
|
||
body.append("fileName", fileName);
|
||
if (contentType) body.append("contentType", contentType);
|
||
const bytes = new Uint8Array(data.byteLength);
|
||
bytes.set(data);
|
||
body.append("file", new Blob([bytes], { type: contentType || "application/octet-stream" }), fileName);
|
||
const res = await fetch(apiUrl("/api/document-store/upload-gridfs-file"), {
|
||
method: "POST",
|
||
body,
|
||
});
|
||
if (!res.ok) throw await backendResponseError(res);
|
||
return res.json();
|
||
}
|
||
|
||
export async function documentDeleteGridFsFile(connectionId: string, database: string, bucket: string, fileId: string): Promise<void> {
|
||
return post("/api/document-store/delete-gridfs-file", {
|
||
connectionId,
|
||
database,
|
||
bucket,
|
||
fileId,
|
||
});
|
||
}
|
||
|
||
export async function mongoServerVersion(connectionId: string, database: string, executionId?: string): Promise<string> {
|
||
return post("/api/mongo/server-version", {
|
||
connectionId,
|
||
database,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function mongoAggregateDocuments(connectionId: string, database: string, collection: string, pipelineJson: string, maxRows?: number, optionsJson?: string, executionId?: string): Promise<MongoDocumentResult> {
|
||
return post("/api/mongo/aggregate-documents", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
pipelineJson,
|
||
maxRows,
|
||
optionsJson,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function mongoDistinct(connectionId: string, database: string, collection: string, field: string, filter?: string, executionId?: string): Promise<MongoDocumentResult> {
|
||
return post("/api/mongo/distinct", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
field,
|
||
filter,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function mongoCollectionStats(connectionId: string, database: string, collection: string, scale?: number, executionId?: string): Promise<MongoCollectionStatsResult> {
|
||
return post("/api/mongo/collection-stats", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
scale,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function mongoListIndexSpecs(connectionId: string, database: string, collection: string): Promise<MongoIndexSpec[]> {
|
||
return post("/api/mongo/list-index-specs", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
});
|
||
}
|
||
|
||
export async function mongoCreateIndex(connectionId: string, database: string, collection: string, keysJson: string, optionsJson?: string): Promise<{ name: string }> {
|
||
return post("/api/mongo/create-index", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
keysJson,
|
||
optionsJson,
|
||
});
|
||
}
|
||
|
||
export async function mongoCreateUser(connectionId: string, database: string, userJson: string, writeConcernJson?: string): Promise<{ affected_rows: number }> {
|
||
return post("/api/mongo/create-user", {
|
||
connectionId,
|
||
database,
|
||
userJson,
|
||
writeConcernJson,
|
||
});
|
||
}
|
||
|
||
export async function mongoRunCommand(connectionId: string, database: string, commandJson: string, executionId?: string): Promise<MongoDocumentResult> {
|
||
return post("/api/mongo/run-command", {
|
||
connectionId,
|
||
database,
|
||
commandJson,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function mongoDropIndexes(connectionId: string, database: string, collection: string, indexesJson?: string, single = false): Promise<MongoDropIndexesResult> {
|
||
return post("/api/mongo/drop-indexes", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
indexesJson,
|
||
single,
|
||
});
|
||
}
|
||
|
||
export async function mongoInsertDocument(connectionId: string, database: string, collection: string, docJson: string, routing?: string): Promise<string> {
|
||
return documentInsertDocument(connectionId, database, collection, docJson, routing);
|
||
}
|
||
|
||
export async function documentInsertDocument(connectionId: string, database: string, collection: string, docJson: string, routing?: string, preserveBsonTypes?: boolean): Promise<string> {
|
||
return post("/api/document-store/insert-document", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
docJson,
|
||
routing,
|
||
preserveBsonTypes,
|
||
});
|
||
}
|
||
|
||
export async function mongoInsertDocuments(connectionId: string, database: string, collection: string, docsJson: string): Promise<{ affected_rows: number }> {
|
||
return post("/api/mongo/insert-documents", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
docsJson,
|
||
});
|
||
}
|
||
|
||
export async function mongoUpdateDocument(connectionId: string, database: string, collection: string, id: string, docJson: string, routing?: string): Promise<number> {
|
||
return documentUpdateDocument(connectionId, database, collection, id, docJson, routing);
|
||
}
|
||
|
||
export async function documentUpdateDocument(connectionId: string, database: string, collection: string, id: string, docJson: string, routing?: string): Promise<number> {
|
||
return post("/api/document-store/update-document", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
id,
|
||
docJson,
|
||
routing,
|
||
});
|
||
}
|
||
|
||
export async function mongoExplainFind(connectionId: string, database: string, collection: string, options: { skip: number; limit: number; filter?: string; projection?: string; sort?: string; collation?: string; verbosity?: string }, executionId?: string): Promise<unknown> {
|
||
return post("/api/mongo/explain-find", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
skip: options.skip,
|
||
limit: options.limit,
|
||
filter: options.filter,
|
||
projection: options.projection,
|
||
sort: options.sort,
|
||
collation: options.collation,
|
||
verbosity: options.verbosity,
|
||
executionId,
|
||
});
|
||
}
|
||
|
||
export async function mongoBulkWrite(connectionId: string, database: string, collection: string, operationsJson: string, optionsJson?: string): Promise<MongoBulkWriteResult> {
|
||
return post("/api/mongo/bulk-write", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
operationsJson,
|
||
optionsJson,
|
||
});
|
||
}
|
||
|
||
export async function mongoReplaceDocument(connectionId: string, database: string, collection: string, filterJson: string, replacementJson: string, optionsJson?: string): Promise<{ affected_rows: number }> {
|
||
return post("/api/mongo/replace-document", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
filterJson,
|
||
replacementJson,
|
||
optionsJson,
|
||
});
|
||
}
|
||
|
||
export async function mongoUpdateDocuments(connectionId: string, database: string, collection: string, filterJson: string, updateJson: string, many: boolean, optionsJson?: string): Promise<{ affected_rows: number }> {
|
||
return post("/api/mongo/update-documents", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
filterJson,
|
||
updateJson,
|
||
many,
|
||
optionsJson,
|
||
});
|
||
}
|
||
|
||
export async function mongoDeleteDocument(connectionId: string, database: string, collection: string, id: string, routing?: string): Promise<number> {
|
||
return documentDeleteDocument(connectionId, database, collection, id, routing);
|
||
}
|
||
|
||
export async function documentDeleteDocument(connectionId: string, database: string, collection: string, id: string, routing?: string, documentType?: string): Promise<number> {
|
||
return post("/api/document-store/delete-document", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
id,
|
||
routing,
|
||
documentType,
|
||
});
|
||
}
|
||
|
||
export async function documentSaveMeilisearchBatch(connectionId: string, collection: string, updates: Array<{ id: string; docJson: string }>, deleteIds: string[], inserts: string[]): Promise<number> {
|
||
return post("/api/document-store/save-meilisearch-batch", {
|
||
connectionId,
|
||
collection,
|
||
updates,
|
||
deleteIds,
|
||
inserts,
|
||
});
|
||
}
|
||
|
||
export async function meilisearchSearchDocuments(
|
||
connectionId: string,
|
||
index: string,
|
||
params: { q?: string | null; filter?: string | null; sort?: string | null; limit: number; offset: number; hybridEmbedder?: string | null; hybridSemanticRatio?: number | null; showRankingScore?: boolean; rankingScoreThreshold?: number | null },
|
||
): Promise<MeilisearchSearchResult> {
|
||
const result = await post<MeilisearchSearchWireResult>("/api/document-store/meilisearch/search", {
|
||
connectionId,
|
||
index,
|
||
q: params.q ?? null,
|
||
filter: params.filter ?? null,
|
||
sort: params.sort ?? null,
|
||
limit: params.limit,
|
||
offset: params.offset,
|
||
hybridEmbedder: params.hybridEmbedder ?? null,
|
||
hybridSemanticRatio: params.hybridSemanticRatio ?? null,
|
||
showRankingScore: params.showRankingScore ?? false,
|
||
rankingScoreThreshold: params.rankingScoreThreshold ?? null,
|
||
});
|
||
return decodeMeilisearchSearchResult(result);
|
||
}
|
||
|
||
export async function meilisearchFetchDocuments(connectionId: string, index: string, params: { filter?: string | null; sort?: string | null; limit: number; offset: number }): Promise<MeilisearchDocumentPage> {
|
||
const page = await post<MeilisearchDocumentPageWire>("/api/document-store/meilisearch/documents/fetch", {
|
||
connectionId,
|
||
index,
|
||
filter: params.filter ?? null,
|
||
sort: params.sort ?? null,
|
||
limit: params.limit,
|
||
offset: params.offset,
|
||
});
|
||
return decodeMeilisearchDocumentPage(page);
|
||
}
|
||
|
||
export async function meilisearchGetDocument(connectionId: string, index: string, id: string): Promise<string> {
|
||
return post("/api/document-store/meilisearch/documents/get", {
|
||
connectionId,
|
||
index,
|
||
id,
|
||
});
|
||
}
|
||
|
||
export async function meilisearchGetIndexSettings(connectionId: string, index: string): Promise<MeilisearchIndexSettings> {
|
||
return post("/api/document-store/meilisearch/settings/get", {
|
||
connectionId,
|
||
index,
|
||
});
|
||
}
|
||
|
||
export async function meilisearchUpdateIndexSettings(connectionId: string, index: string, settings: Record<string, unknown>): Promise<void> {
|
||
return post("/api/document-store/meilisearch/settings/update", {
|
||
connectionId,
|
||
index,
|
||
settings,
|
||
});
|
||
}
|
||
|
||
export async function meilisearchGetIndexStats(connectionId: string, index: string): Promise<{ numberOfDocuments: number; isIndexing: boolean; fieldDistribution: Record<string, number> } & Record<string, unknown>> {
|
||
return post("/api/document-store/meilisearch/stats", {
|
||
connectionId,
|
||
index,
|
||
});
|
||
}
|
||
|
||
export async function meilisearchGetIndexOverview(connectionId: string, index: string): Promise<MeilisearchIndexOverview> {
|
||
return post("/api/document-store/meilisearch/overview", {
|
||
connectionId,
|
||
index,
|
||
});
|
||
}
|
||
|
||
export async function meilisearchCreateIndex(connectionId: string, input: MeilisearchCreateIndexInput): Promise<void> {
|
||
return post("/api/document-store/meilisearch/index/create", { connectionId, input });
|
||
}
|
||
|
||
export async function meilisearchDeleteIndex(connectionId: string, index: string): Promise<void> {
|
||
return post("/api/document-store/meilisearch/index/delete", {
|
||
connectionId,
|
||
index,
|
||
});
|
||
}
|
||
|
||
export async function meilisearchDeleteAllDocuments(connectionId: string, index: string): Promise<void> {
|
||
return post("/api/document-store/meilisearch/documents/delete-all", {
|
||
connectionId,
|
||
index,
|
||
});
|
||
}
|
||
|
||
export async function meilisearchGetSystemOverview(connectionId: string): Promise<MeilisearchSystemOverview> {
|
||
return post("/api/document-store/meilisearch/system/overview", { connectionId });
|
||
}
|
||
|
||
export async function meilisearchListKeys(connectionId: string, offset = 0, limit = 20): Promise<KeyPage> {
|
||
return post("/api/document-store/meilisearch/keys/list", { connectionId, offset, limit });
|
||
}
|
||
|
||
export async function meilisearchGetKey(connectionId: string, uid: string): Promise<KeyListItem> {
|
||
return post("/api/document-store/meilisearch/keys/get", { connectionId, uid });
|
||
}
|
||
|
||
export async function meilisearchCreateKey(connectionId: string, input: KeyCreateInput): Promise<CreatedKey> {
|
||
return post("/api/document-store/meilisearch/keys/create", { connectionId, input });
|
||
}
|
||
|
||
export async function meilisearchUpdateKey(connectionId: string, uid: string, input: KeyUpdateInput): Promise<KeyListItem> {
|
||
return post("/api/document-store/meilisearch/keys/update", { connectionId, uid, input });
|
||
}
|
||
|
||
export async function meilisearchDeleteKey(connectionId: string, uid: string): Promise<void> {
|
||
return post("/api/document-store/meilisearch/keys/delete", { connectionId, uid });
|
||
}
|
||
|
||
export async function meilisearchGetTasks(connectionId: string, input: TaskListInput): Promise<TaskPage> {
|
||
return post("/api/document-store/meilisearch/tasks/list", { connectionId, input });
|
||
}
|
||
|
||
export async function meilisearchGetTask(connectionId: string, uid: number, expectedIndexUid?: string): Promise<MeilisearchTask> {
|
||
return post("/api/document-store/meilisearch/tasks/get", { connectionId, uid, expectedIndexUid: expectedIndexUid ?? null });
|
||
}
|
||
|
||
export async function meilisearchCancelTasks(connectionId: string, selector: TaskSelector): Promise<EnqueuedTaskSummary> {
|
||
return post("/api/document-store/meilisearch/tasks/cancel", { connectionId, selector });
|
||
}
|
||
|
||
export async function meilisearchDeleteTasks(connectionId: string, selector: TaskSelector): Promise<EnqueuedTaskSummary> {
|
||
return post("/api/document-store/meilisearch/tasks/delete", { connectionId, selector });
|
||
}
|
||
|
||
export async function mongoDeleteDocuments(connectionId: string, database: string, collection: string, filterJson: string, many: boolean): Promise<{ affected_rows: number }> {
|
||
return post("/api/mongo/delete-documents", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
filterJson,
|
||
many,
|
||
});
|
||
}
|
||
|
||
export async function mongoFindOneAndUpdate(connectionId: string, database: string, collection: string, filterJson: string, updateJson: string, optionsJson?: string): Promise<MongoDocumentResult> {
|
||
return post("/api/mongo/find-one-and-update", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
filterJson,
|
||
updateJson,
|
||
optionsJson,
|
||
});
|
||
}
|
||
|
||
export async function mongoFindOneAndReplace(connectionId: string, database: string, collection: string, filterJson: string, replacementJson: string, optionsJson?: string): Promise<MongoDocumentResult> {
|
||
return post("/api/mongo/find-one-and-replace", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
filterJson,
|
||
replacementJson,
|
||
optionsJson,
|
||
});
|
||
}
|
||
|
||
export async function mongoFindOneAndDelete(connectionId: string, database: string, collection: string, filterJson: string, optionsJson?: string): Promise<MongoDocumentResult> {
|
||
return post("/api/mongo/find-one-and-delete", {
|
||
connectionId,
|
||
database,
|
||
collection,
|
||
filterJson,
|
||
optionsJson,
|
||
});
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// History
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function saveHistory(entry: HistoryEntry): Promise<void> {
|
||
return post("/api/history/save", { entry });
|
||
}
|
||
|
||
export async function loadHistory(limit: number, offset: number, activityKind?: string): Promise<HistoryEntry[]> {
|
||
return get(`/api/history?${qs({ limit, offset, activity_kind: activityKind })}`);
|
||
}
|
||
|
||
export async function searchHistory(request: HistorySearchRequest): Promise<HistorySearchResult> {
|
||
return post("/api/history/search", request);
|
||
}
|
||
|
||
export async function loadHistoryConnectionOptions(): Promise<HistoryConnectionOption[]> {
|
||
return get("/api/history/options");
|
||
}
|
||
|
||
export async function loadTaskRuns(query: TaskRunListQuery = {}): Promise<TaskRunPage> {
|
||
return get(
|
||
`/api/task-runs?${qs({
|
||
limit: query.limit,
|
||
cursorCreatedAt: query.cursor?.createdAt,
|
||
cursorRunId: query.cursor?.runId,
|
||
taskType: query.taskType,
|
||
status: query.status,
|
||
})}`,
|
||
);
|
||
}
|
||
|
||
export async function loadTaskRun(runId: string): Promise<TaskRunDetail | null> {
|
||
return get(`/api/task-runs/${encodeURIComponent(runId)}`);
|
||
}
|
||
|
||
export async function loadTaskRunItems(runId: string, query: TaskRunItemsQuery = {}): Promise<TaskRunItemsPage> {
|
||
return get(
|
||
`/api/task-runs/${encodeURIComponent(runId)}/items?${qs({
|
||
limit: query.limit,
|
||
afterItemIndex: query.afterItemIndex,
|
||
})}`,
|
||
);
|
||
}
|
||
|
||
export async function loadRedisHistory(limit = 100, offset = 0): Promise<HistoryEntry[]> {
|
||
return loadHistory(limit, offset, "redis_command");
|
||
}
|
||
|
||
export async function clearHistory(): Promise<void> {
|
||
return del("/api/history");
|
||
}
|
||
|
||
export async function clearHistoryBySource(source: string): Promise<void> {
|
||
return del(`/api/history?source=${encodeURIComponent(source)}`);
|
||
}
|
||
|
||
export async function cleanupMcpHistoryRetention(): Promise<number> {
|
||
return post("/api/app-settings/mcp-history-retention-cleanup", {});
|
||
}
|
||
|
||
export async function clearRedisHistory(): Promise<void> {
|
||
const entries = await loadRedisHistory(1000, 0);
|
||
await Promise.all(entries.map((e) => deleteHistoryEntry(e.id)));
|
||
}
|
||
|
||
export async function deleteHistoryEntry(id: string): Promise<void> {
|
||
return del(`/api/history/${id}`);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Updates
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function checkForUpdates(locale?: string, source?: UpdateDownloadSource): Promise<UpdateInfo> {
|
||
const params = new URLSearchParams();
|
||
if (locale) params.set("locale", locale);
|
||
if (source) params.set("source", source);
|
||
const query = params.size > 0 ? `?${params.toString()}` : "";
|
||
return get(`/api/update/check${query}`);
|
||
}
|
||
|
||
export async function fetchChangelog(lang?: string): Promise<import("@/lib/app/changelog").ChangelogData> {
|
||
const query = lang ? `?lang=${encodeURIComponent(lang)}` : "";
|
||
return get(`/api/changelog${query}`);
|
||
}
|
||
|
||
export async function checkMcpServerStatus(): Promise<import("@/lib/backend/tauri").McpServerStatus> {
|
||
return {
|
||
installed: false,
|
||
installation_source: null,
|
||
npm_available: false,
|
||
npm_installed: false,
|
||
node_path: null,
|
||
node_version: null,
|
||
current_version: null,
|
||
latest_version: null,
|
||
update_available: false,
|
||
bin_path: null,
|
||
native_bin_path: null,
|
||
script_path: null,
|
||
data_dir: null,
|
||
install_command: "npm install -g @dbx-app/mcp-server@latest",
|
||
update_command: "npm install -g @dbx-app/mcp-server@latest",
|
||
uninstall_command: "npm uninstall -g @dbx-app/mcp-server",
|
||
error: "MCP Server status is only available in the desktop app.",
|
||
};
|
||
}
|
||
|
||
export async function checkMcpKeychainAccess(): Promise<import("@/lib/backend/tauri").McpKeychainAccess> {
|
||
return "not_applicable";
|
||
}
|
||
|
||
export async function authorizeMcpKeychain(): Promise<string> {
|
||
throw new Error("MCP Keychain authorization is only available in the desktop app.");
|
||
}
|
||
|
||
export async function installMcpServer(): Promise<string> {
|
||
throw new Error("MCP Server installation is only available in the desktop app.");
|
||
}
|
||
|
||
export async function installNativeMcpServer(): Promise<string> {
|
||
throw new Error("Native MCP Server installation is only available in the desktop app.");
|
||
}
|
||
|
||
export async function uninstallMcpServer(): Promise<string> {
|
||
throw new Error("MCP Server uninstallation is only available in the desktop app.");
|
||
}
|
||
|
||
export async function uninstallNpmMcpServer(): Promise<string> {
|
||
throw new Error("MCP Server npm fallback removal is only available in the desktop app.");
|
||
}
|
||
|
||
export async function getSystemProxyUrl(): Promise<string | null> {
|
||
return null;
|
||
}
|
||
|
||
export async function downloadUpdate(_source: UpdateDownloadSource, _latestVersion: string, _attemptId: string, _releaseNotes?: string): Promise<DownloadedUpdate> {
|
||
throw new Error("In-app update downloads are only available in the desktop app.");
|
||
}
|
||
|
||
export async function cancelUpdateDownload(): Promise<void> {}
|
||
|
||
export async function getDownloadedUpdate(): Promise<DownloadedUpdate | null> {
|
||
return null;
|
||
}
|
||
|
||
export async function discardDownloadedUpdate(_cacheId: string): Promise<void> {}
|
||
|
||
export async function installDownloadedUpdate(_cacheId: string, _expectedVersion: string): Promise<void> {
|
||
throw new Error("In-app update installation is only available in the desktop app.");
|
||
}
|
||
|
||
export async function relaunchApp(): Promise<void> {
|
||
throw new Error("Restarting DBX is only available in the desktop app.");
|
||
}
|
||
|
||
export async function getAppVersion(): Promise<string> {
|
||
const res: { version: string } = await get("/api/version");
|
||
return res.version;
|
||
}
|
||
|
||
export async function getAppSupportInfo(): Promise<AppSupportInfo> {
|
||
const appVersion = await getAppVersion();
|
||
return {
|
||
appVersion,
|
||
runtime: "web",
|
||
osName: navigator.platform || "web",
|
||
osVersion: null,
|
||
arch: "",
|
||
userAgent: collectBrowserSupportInfo(),
|
||
};
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Layout
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export async function saveSidebarLayout(layout: SidebarLayout): Promise<void> {
|
||
return post("/api/layout/sidebar", { layout });
|
||
}
|
||
|
||
export async function loadSidebarLayout(): Promise<SidebarLayout | null> {
|
||
return get("/api/layout/sidebar");
|
||
}
|
||
|
||
export async function saveTableVGroups(scopeKey: string, layout: TableVGroupLayout): Promise<void> {
|
||
return post("/api/layout/table-vgroups", { scopeKey, layout });
|
||
}
|
||
|
||
export async function loadTableVGroups(): Promise<Record<string, TableVGroupLayout>> {
|
||
return get("/api/layout/table-vgroups");
|
||
}
|
||
|
||
export async function deleteTableVGroupsForConnection(connectionId: string): Promise<void> {
|
||
return del(`/api/layout/table-vgroups/connection/${encodeURIComponent(connectionId)}`);
|
||
}
|
||
|
||
export async function refreshConnections(): Promise<void> {
|
||
// Web mode doesn't maintain persistent connection pools - no-op
|
||
}
|
||
|
||
export * from "@/lib/backend/mq-http";
|
||
export * from "@/lib/backend/mqtt-http";
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Plugin local file streaming (native dialogs / OS drops are Tauri-only)
|
||
// ---------------------------------------------------------------------------
|
||
|
||
import type { PluginDroppedFilesResult, PluginLocalFileChunk, PluginLocalFileHandle, PluginLocalFileWriteResult } from "./tauri";
|
||
|
||
export async function openDroppedPluginLocalFiles(_pluginId: string, _paths: string[]): Promise<PluginDroppedFilesResult> {
|
||
throw new Error("Plugin local file access is not available in the web backend");
|
||
}
|
||
|
||
export async function pickPluginLocalFiles(_pluginId: string, _multiple: boolean): Promise<PluginLocalFileHandle[]> {
|
||
throw new Error("Plugin local file access is not available in the web backend");
|
||
}
|
||
|
||
export async function savePluginLocalFileAs(_pluginId: string, _defaultFileName: string): Promise<PluginLocalFileHandle | null> {
|
||
throw new Error("Plugin local file access is not available in the web backend");
|
||
}
|
||
|
||
export async function readPluginLocalFileChunk(_pluginId: string, _handleId: string, _offset: number, _length?: number): Promise<PluginLocalFileChunk> {
|
||
throw new Error("Plugin local file access is not available in the web backend");
|
||
}
|
||
|
||
export async function writePluginLocalFileChunk(_pluginId: string, _handleId: string, _offset: number, _dataBase64: string): Promise<PluginLocalFileWriteResult> {
|
||
throw new Error("Plugin local file access is not available in the web backend");
|
||
}
|
||
|
||
export async function closePluginLocalFile(_pluginId: string, _handleId: string): Promise<void> {
|
||
throw new Error("Plugin local file access is not available in the web backend");
|
||
}
|
||
|
||
export async function openPluginMedia(_pluginId: string, _method: string, _params: Record<string, unknown>): Promise<string> {
|
||
throw new Error("Plugin media URLs are not available in the web backend");
|
||
}
|
||
|
||
export async function closePluginMedia(_pluginId: string, _token: string): Promise<void> {
|
||
throw new Error("Plugin media URLs are not available in the web backend");
|
||
}
|
||
|
||
// Plugin UI storage goes through the Rust plugin-data tree on native hosts;
|
||
// the web workbench host keeps its localStorage fallback in the component, so
|
||
// these only exist to satisfy the shared backend surface.
|
||
export async function getPluginUiStorage(_pluginId: string, _key: string): Promise<unknown> {
|
||
throw new Error("Plugin UI storage is not available in the web backend");
|
||
}
|
||
|
||
export async function setPluginUiStorage(_pluginId: string, _key: string, _value: unknown): Promise<void> {
|
||
throw new Error("Plugin UI storage is not available in the web backend");
|
||
}
|
||
|
||
export async function deletePluginUiStorage(_pluginId: string, _key: string): Promise<void> {
|
||
throw new Error("Plugin UI storage is not available in the web backend");
|
||
}
|
||
|
||
export async function openQueryResultTempFile(_path: string): Promise<void> {
|
||
throw new Error("Opening query results requires the desktop app");
|
||
}
|