mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-27 08:48:45 +08:00
- 新增 inspect_recent_sql_activity 总结最近 SQL 活动的读写与报错分布 - 抽离 SQL 日志洞察模块并复用 recent logs 快照逻辑 - 补齐工具目录、系统提示、状态文案与测试覆盖
147 lines
4.0 KiB
TypeScript
147 lines
4.0 KiB
TypeScript
import type { SavedConnection, TabData } from '../../types';
|
|
|
|
const ACTIVE_TAB_CONTENT_LIMIT = 12000;
|
|
const WORKSPACE_TAB_CONTENT_LIMIT = 4000;
|
|
|
|
const normalizeWorkspaceTabLimit = (input: unknown): number => {
|
|
const value = Math.floor(Number(input) || 12);
|
|
if (value < 1) return 1;
|
|
if (value > 30) return 30;
|
|
return value;
|
|
};
|
|
|
|
const resolveContentKind = (tab: TabData, includeContent: boolean, trimmedContent: string): 'sql' | 'command' | 'text' | 'none' => {
|
|
if (!includeContent || !trimmedContent) {
|
|
return 'none';
|
|
}
|
|
if (tab.type === 'query') {
|
|
return 'sql';
|
|
}
|
|
if (tab.type === 'redis-command') {
|
|
return 'command';
|
|
}
|
|
return 'text';
|
|
};
|
|
|
|
const buildTabSnapshot = (params: {
|
|
tab: TabData;
|
|
activeTabId?: string | null;
|
|
connections: SavedConnection[];
|
|
includeContent: boolean;
|
|
contentLimit: number;
|
|
}) => {
|
|
const { tab, activeTabId = null, connections, includeContent, contentLimit } = params;
|
|
const activeConnection = connections.find((connection) => connection.id === tab.connectionId);
|
|
const rawContent =
|
|
tab.type === 'query' || tab.type === 'redis-command'
|
|
? String(tab.query || '')
|
|
: '';
|
|
const trimmedContent = rawContent.trim();
|
|
const visibleContent = includeContent ? trimmedContent.slice(0, contentLimit) : '';
|
|
|
|
return {
|
|
id: tab.id,
|
|
isActive: tab.id === activeTabId,
|
|
title: tab.title,
|
|
type: tab.type,
|
|
connectionId: tab.connectionId,
|
|
connectionName: activeConnection?.name || '',
|
|
connectionType: activeConnection?.config?.type || '',
|
|
dbName: tab.dbName || '',
|
|
tableName: tab.tableName || '',
|
|
filePath: tab.filePath || '',
|
|
readOnly: tab.readOnly === true,
|
|
queryMode: tab.queryMode || '',
|
|
providerMode: tab.providerMode || '',
|
|
resourcePath: tab.resourcePath || '',
|
|
resourceKind: tab.resourceKind || '',
|
|
redisDB: typeof tab.redisDB === 'number' ? tab.redisDB : null,
|
|
schemaName: tab.schemaName || '',
|
|
viewName: tab.viewName || '',
|
|
viewKind: tab.viewKind || '',
|
|
triggerName: tab.triggerName || '',
|
|
eventName: tab.eventName || '',
|
|
routineName: tab.routineName || '',
|
|
routineType: tab.routineType || '',
|
|
contentKind: resolveContentKind(tab, includeContent, trimmedContent),
|
|
content: visibleContent,
|
|
contentCharCount: trimmedContent.length,
|
|
contentTruncated: includeContent && trimmedContent.length > visibleContent.length,
|
|
};
|
|
};
|
|
|
|
export const buildActiveTabSnapshot = (params: {
|
|
tabs?: TabData[];
|
|
activeTabId?: string | null;
|
|
connections: SavedConnection[];
|
|
includeContent?: boolean;
|
|
}) => {
|
|
const {
|
|
tabs = [],
|
|
activeTabId = null,
|
|
connections,
|
|
includeContent = true,
|
|
} = params;
|
|
const activeTab = tabs.find((tab) => tab.id === activeTabId);
|
|
if (!activeTab) {
|
|
return {
|
|
hasActiveTab: false,
|
|
message: '当前没有活动页签',
|
|
};
|
|
}
|
|
|
|
return {
|
|
hasActiveTab: true,
|
|
tabId: activeTab.id,
|
|
...buildTabSnapshot({
|
|
tab: activeTab,
|
|
activeTabId,
|
|
connections,
|
|
includeContent,
|
|
contentLimit: ACTIVE_TAB_CONTENT_LIMIT,
|
|
}),
|
|
};
|
|
};
|
|
|
|
export const buildWorkspaceTabsSnapshot = (params: {
|
|
tabs?: TabData[];
|
|
activeTabId?: string | null;
|
|
connections: SavedConnection[];
|
|
includeContent?: boolean;
|
|
limit?: unknown;
|
|
}) => {
|
|
const {
|
|
tabs = [],
|
|
activeTabId = null,
|
|
connections,
|
|
includeContent = false,
|
|
limit,
|
|
} = params;
|
|
const safeLimit = normalizeWorkspaceTabLimit(limit);
|
|
const orderedTabs = [...tabs].sort((left, right) => {
|
|
if (left.id === activeTabId && right.id !== activeTabId) {
|
|
return -1;
|
|
}
|
|
if (right.id === activeTabId && left.id !== activeTabId) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
const visibleTabs = orderedTabs.slice(0, safeLimit);
|
|
|
|
return {
|
|
activeTabId,
|
|
totalTabs: orderedTabs.length,
|
|
returnedTabs: visibleTabs.length,
|
|
truncated: orderedTabs.length > visibleTabs.length,
|
|
tabs: visibleTabs.map((tab) =>
|
|
buildTabSnapshot({
|
|
tab,
|
|
activeTabId,
|
|
connections,
|
|
includeContent,
|
|
contentLimit: WORKSPACE_TAB_CONTENT_LIMIT,
|
|
})),
|
|
};
|
|
};
|