feat(frontend): 增强工作台快捷指令与仪表盘能力
补充快捷指令动态变量解析与编辑弹窗一键插入, 统一列表执行、粘贴到终端和批量发送的处理链路 扩展快捷命令右键菜单动作,并为文件面板新增 多根目录资源管理器式侧栏浏览体验 为首页 dashboard 增加当前用户与系统总览双视角的 实时会话指标展示,并同步更新相关知识库记录
This commit is contained in:
@@ -6,7 +6,7 @@ const dashboardService = new DashboardService();
|
||||
export class DashboardController {
|
||||
async getSummary(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const summary = await dashboardService.getSummary();
|
||||
const summary = await dashboardService.getSummary(req.session.userId);
|
||||
res.status(200).json(summary);
|
||||
} catch (error: any) {
|
||||
console.error('[DashboardController] 获取仪表盘统计失败:', error);
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
DashboardActionBreakdownItem,
|
||||
DashboardActivityTrendPoint,
|
||||
DashboardCountByType,
|
||||
DashboardSummary,
|
||||
DashboardStaticSummary,
|
||||
DashboardTopConnection,
|
||||
} from './dashboard.types';
|
||||
import type { AuditLogActionType } from '../types/audit.types';
|
||||
@@ -75,7 +75,7 @@ const safeParseAuditDetails = (raw: string | null): ParsedAuditDetails | null =>
|
||||
}
|
||||
};
|
||||
|
||||
export const getDashboardSummary = async (): Promise<DashboardSummary> => {
|
||||
export const getDashboardSummary = async (): Promise<DashboardStaticSummary> => {
|
||||
const db = await getDbInstance();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const since7d = now - (DASHBOARD_WINDOW_DAYS - 1) * DAY_IN_SECONDS;
|
||||
|
||||
@@ -1,8 +1,32 @@
|
||||
import { getDashboardSummary } from './dashboard.repository';
|
||||
import type { DashboardSummary } from './dashboard.types';
|
||||
import { clientStates } from '../websocket/state';
|
||||
import { sshSuspendService } from '../ssh-suspend/ssh-suspend.service';
|
||||
|
||||
export class DashboardService {
|
||||
async getSummary(): Promise<DashboardSummary> {
|
||||
return getDashboardSummary();
|
||||
async getSummary(userId?: number): Promise<DashboardSummary> {
|
||||
const summary = await getDashboardSummary();
|
||||
const activeStates = Array.from(clientStates.values()).filter((state) => !state.isSuspendedByService);
|
||||
const systemActiveSshSessions = activeStates.length;
|
||||
const systemStatusStreams = activeStates.filter((state) => !!state.statusIntervalId).length;
|
||||
const currentUserActiveSshSessions = typeof userId === 'number'
|
||||
? activeStates.filter((state) => state.ws.userId === userId).length
|
||||
: 0;
|
||||
const suspendedMetrics = sshSuspendService.getSessionMetrics(userId);
|
||||
|
||||
return {
|
||||
...summary,
|
||||
liveMetrics: {
|
||||
currentUser: {
|
||||
activeSshSessions: currentUserActiveSshSessions,
|
||||
suspendedSessions: suspendedMetrics.currentUserSuspendedSessions,
|
||||
},
|
||||
system: {
|
||||
activeSshSessions: systemActiveSshSessions,
|
||||
suspendedSessions: suspendedMetrics.totalSuspendedSessions,
|
||||
statusStreams: systemStatusStreams,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,23 @@ export interface DashboardActionBreakdownItem {
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface DashboardSummary {
|
||||
export interface DashboardCurrentUserLiveMetrics {
|
||||
activeSshSessions: number;
|
||||
suspendedSessions: number;
|
||||
}
|
||||
|
||||
export interface DashboardSystemLiveMetrics {
|
||||
activeSshSessions: number;
|
||||
suspendedSessions: number;
|
||||
statusStreams: number;
|
||||
}
|
||||
|
||||
export interface DashboardLiveMetrics {
|
||||
currentUser: DashboardCurrentUserLiveMetrics;
|
||||
system: DashboardSystemLiveMetrics;
|
||||
}
|
||||
|
||||
export interface DashboardStaticSummary {
|
||||
totals: DashboardTotals;
|
||||
sshOutcomes24h: DashboardSshOutcomes24h;
|
||||
connectionTypes: DashboardCountByType[];
|
||||
@@ -43,3 +59,7 @@ export interface DashboardSummary {
|
||||
activityTrend7d: DashboardActivityTrendPoint[];
|
||||
topConnections: DashboardTopConnection[];
|
||||
}
|
||||
|
||||
export interface DashboardSummary extends DashboardStaticSummary {
|
||||
liveMetrics: DashboardLiveMetrics;
|
||||
}
|
||||
|
||||
@@ -211,6 +211,28 @@ export class SshSuspendService extends EventEmitter {
|
||||
return sessionsInfo;
|
||||
}
|
||||
|
||||
getSessionMetrics(userId?: number): {
|
||||
totalSuspendedSessions: number;
|
||||
currentUserSuspendedSessions: number;
|
||||
} {
|
||||
let totalSuspendedSessions = 0;
|
||||
let currentUserSuspendedSessions = 0;
|
||||
|
||||
for (const [ownerUserId, sessions] of this.suspendedSessions.entries()) {
|
||||
const sessionCount = sessions.size;
|
||||
totalSuspendedSessions += sessionCount;
|
||||
|
||||
if (typeof userId === 'number' && ownerUserId === userId) {
|
||||
currentUserSuspendedSessions += sessionCount;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalSuspendedSessions,
|
||||
currentUserSuspendedSessions,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复指定的挂起会话。
|
||||
* @param userId 用户ID。
|
||||
@@ -450,4 +472,4 @@ export class SshSuspendService extends EventEmitter {
|
||||
}
|
||||
|
||||
// 单例模式导出
|
||||
export const sshSuspendService = new SshSuspendService();
|
||||
export const sshSuspendService = new SshSuspendService();
|
||||
|
||||
Reference in New Issue
Block a user