- 区分服务端和客户端版本,避免频繁更新客户端
- 优化前端代码
This commit is contained in:
1 parent
33e4093461
commit
def7b069d2
32 files changed
+262
-212
No files matched your search
@@ -1,11 +1,25 @@
|
||||
# 版本号(可通过 make VERSION=1.0.0 指定)
|
||||
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
||||
|
||||
# Agent 版本号基于 pkg/agent/ 目录的最后修改提交生成
|
||||
# 格式:agent-{最近修改 pkg/agent 的提交短hash}-{提交数量}
|
||||
AGENT_VERSION ?= $(shell \
|
||||
LAST_COMMIT=$$(git log -1 --format=%h -- pkg/agent 2>/dev/null || echo "dev"); \
|
||||
COMMIT_COUNT=$$(git rev-list --count HEAD -- pkg/agent 2>/dev/null || echo "0"); \
|
||||
if [ "$$LAST_COMMIT" = "dev" ]; then \
|
||||
echo "dev"; \
|
||||
else \
|
||||
echo "agent-$$LAST_COMMIT-$$COMMIT_COUNT"; \
|
||||
fi \
|
||||
)
|
||||
|
||||
GIT_REVISION=$(shell git rev-parse HEAD)
|
||||
GO_VERSION=$(shell go version)
|
||||
BUILD_TIME=$(shell date +%Y-%m-%d_%H:%M:%S)
|
||||
|
||||
# Go 构建参数
|
||||
LDFLAGS=-s -w -X 'github.com/dushixiang/pika/pkg/version.Version=$(VERSION)'
|
||||
AGENT_LDFLAGS=-s -w -X 'github.com/dushixiang/pika/pkg/version.Version=$(VERSION)' -X 'github.com/dushixiang/pika/pkg/version.AgentVersion=$(AGENT_VERSION)'
|
||||
GOFLAGS=CGO_ENABLED=0
|
||||
|
||||
# 构建前端
|
||||
@@ -26,21 +40,23 @@ build-servers:
|
||||
|
||||
# 构建所有平台的 Agent
|
||||
build-agents:
|
||||
@echo "Building agents for all platforms (version: $(VERSION))..."
|
||||
@echo "Building agents for all platforms..."
|
||||
@echo "Server version: $(VERSION)"
|
||||
@echo "Agent version: $(AGENT_VERSION)"
|
||||
@mkdir -p bin/agents
|
||||
|
||||
# Linux
|
||||
$(GOFLAGS) GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-linux-amd64 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=linux GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-linux-arm64 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-linux-armv7 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=linux GOARCH=loong64 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-linux-loong64 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=linux GOARCH=amd64 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-linux-amd64 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=linux GOARCH=arm64 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-linux-arm64 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-linux-armv7 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=linux GOARCH=loong64 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-linux-loong64 cmd/agent/*.go
|
||||
|
||||
# macOS
|
||||
$(GOFLAGS) GOOS=darwin GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-darwin-amd64 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=darwin GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-darwin-arm64 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=darwin GOARCH=amd64 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-darwin-amd64 cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=darwin GOARCH=arm64 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-darwin-arm64 cmd/agent/*.go
|
||||
|
||||
# Windows
|
||||
$(GOFLAGS) GOOS=windows GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-windows-amd64.exe cmd/agent/*.go
|
||||
$(GOFLAGS) GOOS=windows GOARCH=amd64 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-windows-amd64.exe cmd/agent/*.go
|
||||
|
||||
@echo "All agents built successfully!"
|
||||
@echo "Compressing agents with UPX..."
|
||||
@@ -80,7 +96,8 @@ wire:
|
||||
|
||||
# 显示版本信息
|
||||
version:
|
||||
@echo "Version: $(VERSION)"
|
||||
@echo "Server Version: $(VERSION)"
|
||||
@echo "Agent Version: $(AGENT_VERSION)"
|
||||
@echo "Git Revision: $(GIT_REVISION)"
|
||||
@echo "Go Version: $(GO_VERSION)"
|
||||
@echo "Build Time: $(BUILD_TIME)"
|
||||
@@ -552,7 +552,7 @@ func (h *AgentHandler) GetAgents(c echo.Context) error {
|
||||
// GetAgentVersion 获取 Agent 版本信息
|
||||
func (h *AgentHandler) GetAgentVersion(c echo.Context) error {
|
||||
return orz.Ok(c, orz.Map{
|
||||
"version": version.GetVersion(),
|
||||
"version": version.GetAgentVersion(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -587,9 +587,9 @@ func (a *Agent) sendCommandResponse(conn *safeConn, cmdID, cmdType, status, errM
|
||||
}
|
||||
}
|
||||
|
||||
// GetVersion 获取版本号
|
||||
// GetVersion 获取 Agent 版本号
|
||||
func GetVersion() string {
|
||||
return version.GetVersion()
|
||||
return version.GetAgentVersion()
|
||||
}
|
||||
|
||||
// handleTamperProtect 处理防篡改保护指令(增量更新)
|
||||
|
||||
+13
-2
@@ -1,12 +1,23 @@
|
||||
package version
|
||||
|
||||
// Version 版本号(通过 -ldflags 注入)
|
||||
// Version 服务端版本号(通过 -ldflags 注入)
|
||||
var Version = "dev"
|
||||
|
||||
// GetVersion 获取版本号
|
||||
// AgentVersion Agent 版本号(通过 -ldflags 注入)
|
||||
var AgentVersion = "dev"
|
||||
|
||||
// GetVersion 获取服务端版本号
|
||||
func GetVersion() string {
|
||||
if Version == "" {
|
||||
return "dev"
|
||||
}
|
||||
return Version
|
||||
}
|
||||
|
||||
// GetAgentVersion 获取 Agent 版本号
|
||||
func GetAgentVersion() string {
|
||||
if AgentVersion == "" {
|
||||
return "dev"
|
||||
}
|
||||
return AgentVersion
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { get, post, put } from './request';
|
||||
import type { NotificationChannel } from '../types';
|
||||
|
||||
const PROPERTY_ID_NOTIFICATION_CHANNELS = 'notification_channels';
|
||||
|
||||
// 获取通知渠道列表
|
||||
export const getNotificationChannels = async (): Promise<NotificationChannel[]> => {
|
||||
const response = await get<{ id: string; name: string; value: NotificationChannel[] }>(
|
||||
`/admin/properties/${PROPERTY_ID_NOTIFICATION_CHANNELS}`
|
||||
);
|
||||
return response.data.value || [];
|
||||
};
|
||||
|
||||
// 保存通知渠道列表
|
||||
export const saveNotificationChannels = async (channels: NotificationChannel[]): Promise<void> => {
|
||||
await put(`/admin/properties/${PROPERTY_ID_NOTIFICATION_CHANNELS}`, {
|
||||
name: '通知渠道配置',
|
||||
value: channels,
|
||||
});
|
||||
};
|
||||
|
||||
// 测试通知渠道(从数据库读取配置)
|
||||
export const testNotificationChannel = async (type: string): Promise<{ message: string }> => {
|
||||
const response = await post<{ message: string }>(`/admin/notification-channels/${type}/test`);
|
||||
return response.data;
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
import { get, post, put } from './request';
|
||||
|
||||
// ==================== 通用 Property 接口 ====================
|
||||
|
||||
// 通用的 Property 响应类型
|
||||
export interface PropertyResponse<T> {
|
||||
id: string;
|
||||
name: string;
|
||||
value: T;
|
||||
}
|
||||
|
||||
// 通用的获取 Property 方法
|
||||
export const getProperty = async <T>(propertyId: string): Promise<T> => {
|
||||
const response = await get<PropertyResponse<T>>(`/admin/properties/${propertyId}`);
|
||||
return response.data.value;
|
||||
};
|
||||
|
||||
// 通用的保存 Property 方法
|
||||
export const saveProperty = async <T>(propertyId: string, name: string, value: T): Promise<void> => {
|
||||
await put(`/admin/properties/${propertyId}`, {
|
||||
name,
|
||||
value,
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 指标配置 ====================
|
||||
|
||||
const PROPERTY_ID_METRICS_CONFIG = 'metrics_config';
|
||||
|
||||
export interface MetricsConfig {
|
||||
retentionHours: number; // 数据保留时长(小时)
|
||||
maxQueryPoints: number; // 最大查询点数
|
||||
}
|
||||
|
||||
// 获取指标配置
|
||||
export const getMetricsConfig = async (): Promise<MetricsConfig> => {
|
||||
return getProperty<MetricsConfig>(PROPERTY_ID_METRICS_CONFIG);
|
||||
};
|
||||
|
||||
// 保存指标配置
|
||||
export const saveMetricsConfig = async (config: MetricsConfig): Promise<void> => {
|
||||
return saveProperty(PROPERTY_ID_METRICS_CONFIG, '指标配置', config);
|
||||
};
|
||||
|
||||
// ==================== 通知渠道配置 ====================
|
||||
|
||||
const PROPERTY_ID_NOTIFICATION_CHANNELS = 'notification_channels';
|
||||
|
||||
// 通知渠道配置(通过 type 标识,不再使用独立ID)
|
||||
export interface NotificationChannel {
|
||||
type: 'dingtalk' | 'wecom' | 'feishu' | 'email' | 'webhook'; // 渠道类型,作为唯一标识
|
||||
enabled: boolean; // 是否启用
|
||||
config: Record<string, any>; // JSON配置,根据type不同而不同
|
||||
}
|
||||
|
||||
// 获取通知渠道列表
|
||||
export const getNotificationChannels = async (): Promise<NotificationChannel[]> => {
|
||||
const channels = await getProperty<NotificationChannel[]>(PROPERTY_ID_NOTIFICATION_CHANNELS);
|
||||
return channels || [];
|
||||
};
|
||||
|
||||
// 保存通知渠道列表
|
||||
export const saveNotificationChannels = async (channels: NotificationChannel[]): Promise<void> => {
|
||||
return saveProperty(PROPERTY_ID_NOTIFICATION_CHANNELS, '通知渠道配置', channels);
|
||||
};
|
||||
|
||||
// 测试通知渠道(从数据库读取配置)
|
||||
export const testNotificationChannel = async (type: string): Promise<{ message: string }> => {
|
||||
const response = await post<{ message: string }>(`/admin/notification-channels/${type}/test`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// ==================== 系统配置 ====================
|
||||
|
||||
const PROPERTY_ID_SYSTEM_CONFIG = 'system_config';
|
||||
|
||||
export interface SystemConfig {
|
||||
systemNameEn: string; // 英文名称
|
||||
systemNameZh: string; // 中文名称
|
||||
logoBase64: string; // Logo 的 base64 编码
|
||||
icpCode: string; // ICP 备案号
|
||||
defaultView: string; // 默认视图 grid,list
|
||||
}
|
||||
|
||||
// 获取系统配置(管理后台使用)
|
||||
export const getSystemConfig = async (): Promise<SystemConfig> => {
|
||||
return getProperty<SystemConfig>(PROPERTY_ID_SYSTEM_CONFIG);
|
||||
};
|
||||
|
||||
// 保存系统配置
|
||||
export const saveSystemConfig = async (config: SystemConfig): Promise<void> => {
|
||||
return saveProperty(PROPERTY_ID_SYSTEM_CONFIG, '系统配置', config);
|
||||
};
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import {get, put} from './request';
|
||||
|
||||
const PROPERTY_ID_SYSTEM_CONFIG = 'system_config';
|
||||
|
||||
export interface SystemConfig {
|
||||
systemNameEn: string; // 英文名称
|
||||
systemNameZh: string; // 中文名称
|
||||
logoBase64: string; // Logo 的 base64 编码
|
||||
icpCode: string; // ICP 备案号
|
||||
defaultView: string; // 默认视图 grid,list
|
||||
}
|
||||
|
||||
// 获取系统配置(管理后台使用)
|
||||
export const getSystemConfig = async (): Promise<SystemConfig> => {
|
||||
const response = await get<{ id: string; name: string; value: SystemConfig }>(
|
||||
`/admin/properties/${PROPERTY_ID_SYSTEM_CONFIG}`
|
||||
);
|
||||
return response.data.value;
|
||||
};
|
||||
|
||||
// 保存系统配置
|
||||
export const saveSystemConfig = async (config: SystemConfig): Promise<void> => {
|
||||
await put(`/admin/properties/${PROPERTY_ID_SYSTEM_CONFIG}`, {
|
||||
name: '系统配置',
|
||||
value: config,
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Button, Space} from 'antd';
|
||||
import {Button} from 'antd';
|
||||
import type {LucideIcon} from 'lucide-react';
|
||||
|
||||
export interface Action {
|
||||
|
||||
@@ -3,10 +3,10 @@ import {Outlet, useLocation, useNavigate} from 'react-router-dom';
|
||||
import type {MenuProps} from 'antd';
|
||||
import {App, Avatar, Button, Dropdown, Space} from 'antd';
|
||||
import {Activity, BookOpen, Eye, Key, LogOut, Server, Settings, User as UserIcon} from 'lucide-react';
|
||||
import {logout} from '../../api/auth';
|
||||
import {getServerVersion} from '../../api/agent';
|
||||
import type {User} from '../../types';
|
||||
import {cn} from '../../lib/utils';
|
||||
import {logout} from '@/api/auth.ts';
|
||||
import {getServerVersion} from '@/api/agent.ts';
|
||||
import type {User} from '@/types';
|
||||
import {cn} from '@/lib/utils';
|
||||
|
||||
interface NavItem {
|
||||
key: string;
|
||||
|
||||
@@ -1,30 +1,13 @@
|
||||
import {useEffect, useState} from 'react';
|
||||
import {useNavigate, useParams, useSearchParams} from 'react-router-dom';
|
||||
import type {MenuProps, TabsProps} from 'antd';
|
||||
import {App, Button, Card, Col, Descriptions, Dropdown, Row, Space, Spin, Tag, Tabs, Alert, Statistic} from 'antd';
|
||||
import {
|
||||
Activity,
|
||||
ArrowLeft,
|
||||
Clock,
|
||||
RefreshCw,
|
||||
Shield,
|
||||
Terminal,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
AlertTriangle,
|
||||
TrendingUp,
|
||||
FileWarning
|
||||
} from 'lucide-react';
|
||||
import {Alert, App, Button, Card, Descriptions, Dropdown, Space, Spin, Tabs, Tag} from 'antd';
|
||||
import {Activity, ArrowLeft, Clock, FileWarning, RefreshCw, Shield, Terminal} from 'lucide-react';
|
||||
import TamperProtection from './TamperProtection.tsx';
|
||||
import {
|
||||
getAgentForAdmin,
|
||||
getAuditResult,
|
||||
sendAuditCommand,
|
||||
type VPSAuditResult
|
||||
} from '../../api/agent';
|
||||
import type {Agent} from '../../types';
|
||||
import {getAgentForAdmin, getAuditResult, sendAuditCommand, type VPSAuditResult} from '@/api/agent.ts';
|
||||
import type {Agent} from '@/types';
|
||||
import dayjs from 'dayjs';
|
||||
import {getErrorMessage} from '../../lib/utils';
|
||||
import {getErrorMessage} from '@/lib/utils';
|
||||
import AuditResultView from './AuditResultView';
|
||||
|
||||
const AgentDetail = () => {
|
||||
@@ -235,7 +218,7 @@ const AgentDetail = () => {
|
||||
</div>
|
||||
),
|
||||
children: agent?.os.toLowerCase().includes('linux') ? (
|
||||
<TamperProtection agentId={agent.id} />
|
||||
<TamperProtection agentId={agent.id}/>
|
||||
) : (
|
||||
<Alert
|
||||
message="功能限制"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, {type ReactElement, useCallback, useEffect, useMemo, useState} from 'react';
|
||||
import {Alert, App, Button, Card, Select, Space, Tabs, Typography} from 'antd';
|
||||
import {CopyIcon} from 'lucide-react';
|
||||
import {listApiKeys} from '../../api/apiKey';
|
||||
import type {ApiKey} from '../../types';
|
||||
import {listApiKeys} from '@/api/apiKey.ts';
|
||||
import type {ApiKey} from '@/types';
|
||||
import linuxPng from '../../assets/os/linux.png';
|
||||
import applePng from '../../assets/os/apple.png';
|
||||
import windowsPng from '../../assets/os/win11.png';
|
||||
@@ -377,7 +377,7 @@ curl -L ${serverUrl}${config.downloadUrl} -o ${AGENT_NAME}`
|
||||
</div>
|
||||
|
||||
<Tabs
|
||||
style={{ marginTop: 24 }}
|
||||
style={{marginTop: 24}}
|
||||
tabPosition="left"
|
||||
items={tabItems}
|
||||
/>
|
||||
|
||||
@@ -5,11 +5,11 @@ import {ProTable} from '@ant-design/pro-components';
|
||||
import {App, Button, DatePicker, Divider, Dropdown, Form, Input, Modal, Select, Space, Tag} from 'antd';
|
||||
import type {MenuProps} from 'antd';
|
||||
import {Edit, Eye, RefreshCw, Plus, Shield, Trash2, MoreVertical} from 'lucide-react';
|
||||
import {deleteAgent, getAgentPaging, getTags, updateAgentInfo} from '../../api/agent';
|
||||
import type {Agent} from '../../types';
|
||||
import {getErrorMessage} from '../../lib/utils';
|
||||
import {deleteAgent, getAgentPaging, getTags, updateAgentInfo} from '@/api/agent.ts';
|
||||
import type {Agent} from '@/types';
|
||||
import {getErrorMessage} from '@/lib/utils';
|
||||
import dayjs from 'dayjs';
|
||||
import {PageHeader} from '../../components';
|
||||
import {PageHeader} from '@/components';
|
||||
|
||||
const AgentList = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
import {Card, Collapse, List, Space, Tag, Descriptions, Tabs, Table, Empty, Alert} from 'antd';
|
||||
import {Alert, Card, Descriptions, Empty, List, Space, Table, Tabs, Tag} from 'antd';
|
||||
import {
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
AlertTriangle,
|
||||
MinusCircle,
|
||||
Server,
|
||||
Network,
|
||||
Cpu,
|
||||
Users,
|
||||
FileText,
|
||||
Shield,
|
||||
Activity,
|
||||
Calendar,
|
||||
Settings,
|
||||
PlayCircle,
|
||||
AlertOctagon,
|
||||
LogIn
|
||||
AlertTriangle,
|
||||
Calendar,
|
||||
CheckCircle,
|
||||
Cpu,
|
||||
FileText,
|
||||
LogIn,
|
||||
MinusCircle,
|
||||
Network,
|
||||
PlayCircle,
|
||||
Server,
|
||||
Settings,
|
||||
Shield,
|
||||
Users,
|
||||
XCircle
|
||||
} from 'lucide-react';
|
||||
import type {VPSAuditResult, ProcessInfo, FileInfo, LoginRecord, LoginSession, SSHKeyInfo, SudoUserInfo} from '../../api/agent';
|
||||
import type {ProcessInfo, VPSAuditResult} from '@/api/agent.ts';
|
||||
import dayjs from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import React from 'react';
|
||||
|
||||
dayjs.extend(duration);
|
||||
|
||||
const {Panel} = Collapse;
|
||||
|
||||
interface RiskItem {
|
||||
level: 'critical' | 'high' | 'medium' | 'low';
|
||||
title: string;
|
||||
@@ -472,7 +470,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
||||
{/* 风险概览 */}
|
||||
{risks.length > 0 && (
|
||||
<Card
|
||||
title={<Space><AlertOctagon size={18} className="text-red-500"/><span className="font-semibold text-red-500">风险概览</span></Space>}
|
||||
title={<Space><AlertOctagon size={18} className="text-red-500"/><span
|
||||
className="font-semibold text-red-500">风险概览</span></Space>}
|
||||
className="border-red-200 bg-red-50"
|
||||
>
|
||||
<List
|
||||
@@ -592,7 +591,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
<Card size="small" title={<Space><AlertTriangle size={16} className={result.assetInventory.processAssets?.suspiciousProcesses?.length ? "text-red-500" : ""}/>可疑进程</Space>}>
|
||||
<Card size="small" title={<Space><AlertTriangle size={16}
|
||||
className={result.assetInventory.processAssets?.suspiciousProcesses?.length ? "text-red-500" : ""}/>可疑进程</Space>}>
|
||||
{result.assetInventory.processAssets?.suspiciousProcesses?.length ? (
|
||||
<Table
|
||||
size="small"
|
||||
@@ -602,7 +602,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
||||
{
|
||||
title: '异常信息',
|
||||
key: 'issue',
|
||||
render: (record: ProcessInfo) => record.exeDeleted ? <Tag color="error">Exe已删除</Tag> : null
|
||||
render: (record: ProcessInfo) => record.exeDeleted ?
|
||||
<Tag color="error">Exe已删除</Tag> : null
|
||||
}
|
||||
]}
|
||||
rowKey="pid"
|
||||
@@ -767,15 +768,26 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
<Card size="small" title={<Space><AlertTriangle size={16} className={result.assetInventory.fileAssets?.tmpExecutables?.length ? "text-red-500" : ""}/>临时目录可执行文件</Space>}>
|
||||
<Card size="small" title={<Space><AlertTriangle size={16}
|
||||
className={result.assetInventory.fileAssets?.tmpExecutables?.length ? "text-red-500" : ""}/>临时目录可执行文件</Space>}>
|
||||
{result.assetInventory.fileAssets?.tmpExecutables?.length ? (
|
||||
<Table
|
||||
size="small"
|
||||
dataSource={result.assetInventory.fileAssets.tmpExecutables}
|
||||
columns={[
|
||||
{title: '路径', dataIndex: 'path', key: 'path'},
|
||||
{title: '大小', dataIndex: 'size', key: 'size', render: (val: number) => `${(val / 1024).toFixed(2)} KB`},
|
||||
{title: '修改时间', dataIndex: 'modTime', key: 'modTime', render: (val: number) => dayjs(val).format('YYYY-MM-DD HH:mm:ss')},
|
||||
{
|
||||
title: '大小',
|
||||
dataIndex: 'size',
|
||||
key: 'size',
|
||||
render: (val: number) => `${(val / 1024).toFixed(2)} KB`
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'modTime',
|
||||
key: 'modTime',
|
||||
render: (val: number) => dayjs(val).format('YYYY-MM-DD HH:mm:ss')
|
||||
},
|
||||
{title: '权限', dataIndex: 'permissions', key: 'permissions'},
|
||||
]}
|
||||
rowKey="path"
|
||||
@@ -825,7 +837,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
||||
pagination={false}
|
||||
/>
|
||||
) : (
|
||||
<Empty description={showAllServices ? "无Systemd服务" : "无活跃的Systemd服务"}/>
|
||||
<Empty
|
||||
description={showAllServices ? "无Systemd服务" : "无活跃的Systemd服务"}/>
|
||||
)}
|
||||
</Card>
|
||||
<Card size="small" title={<Space><PlayCircle size={16}/>启动脚本</Space>}>
|
||||
@@ -870,7 +883,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
||||
<Empty description="无登录历史"/>
|
||||
)}
|
||||
</Card>
|
||||
<Card size="small" title={<Space><AlertTriangle size={16} className={result.assetInventory.loginAssets?.failedLogins?.length ? "text-yellow-500" : ""}/>失败登录历史</Space>}>
|
||||
<Card size="small" title={<Space><AlertTriangle size={16}
|
||||
className={result.assetInventory.loginAssets?.failedLogins?.length ? "text-yellow-500" : ""}/>失败登录历史</Space>}>
|
||||
{result.assetInventory.loginAssets?.failedLogins?.length ? (
|
||||
<Table
|
||||
size="small"
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
type TamperConfig,
|
||||
type TamperEvent,
|
||||
updateTamperConfig
|
||||
} from '../../api/tamper.ts';
|
||||
} from '@/api/tamper.ts';
|
||||
import {App} from "antd";
|
||||
|
||||
interface TamperProtectionProps {
|
||||
|
||||
@@ -10,11 +10,11 @@ import {
|
||||
generateApiKey,
|
||||
listApiKeys,
|
||||
updateApiKeyName,
|
||||
} from '../../api/apiKey';
|
||||
import type {ApiKey, GenerateApiKeyRequest} from '../../types';
|
||||
} from '@/api/apiKey.ts';
|
||||
import type {ApiKey, GenerateApiKeyRequest} from '@/types';
|
||||
import dayjs from 'dayjs';
|
||||
import {getErrorMessage} from '../../lib/utils';
|
||||
import {PageHeader} from '../../components';
|
||||
import {getErrorMessage} from '@/lib/utils';
|
||||
import {PageHeader} from '@/components';
|
||||
|
||||
const ApiKeyList = () => {
|
||||
const {message: messageApi} = App.useApp();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useEffect} from 'react';
|
||||
import {useNavigate, useSearchParams} from 'react-router-dom';
|
||||
import {App} from 'antd';
|
||||
import {githubLogin} from '../../api/auth';
|
||||
import {githubLogin} from '@/api/auth.ts';
|
||||
|
||||
const GitHubCallback = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useEffect} from 'react';
|
||||
import {useNavigate, useSearchParams} from 'react-router-dom';
|
||||
import {App} from 'antd';
|
||||
import {oidcLogin} from '../../api/auth';
|
||||
import {oidcLogin} from '@/api/auth.ts';
|
||||
|
||||
const OIDCCallback = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -2,9 +2,8 @@ import {useEffect, useState} from 'react';
|
||||
import {useNavigate} from 'react-router-dom';
|
||||
import {App, Button, Form, Input} from 'antd';
|
||||
import {GithubOutlined, GlobalOutlined, LockOutlined, UserOutlined} from '@ant-design/icons';
|
||||
// 移除不需要的图标组件,保持轻量
|
||||
import {getAuthConfig, getGitHubAuthURL, getOIDCAuthURL, login} from '../../api/auth';
|
||||
import type {LoginRequest} from '../../types';
|
||||
import {getAuthConfig, getGitHubAuthURL, getOIDCAuthURL, login} from '@/api/auth.ts';
|
||||
import type {LoginRequest} from '@/types';
|
||||
|
||||
const Login = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -2,13 +2,13 @@ import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
||||
import type {ActionType, ProColumns} from '@ant-design/pro-components';
|
||||
import {ProTable} from '@ant-design/pro-components';
|
||||
import {App, Button, Divider, Form, Input, InputNumber, Modal, Select, Space, Switch, Tag,} from 'antd';
|
||||
import {PageHeader} from '../../components';
|
||||
import {PageHeader} from '@/components';
|
||||
import {Edit, MinusCircle, Plus, PlusCircle, RefreshCw, Trash2} from 'lucide-react';
|
||||
import dayjs from 'dayjs';
|
||||
import {getAgentPaging, getTags} from '../../api/agent';
|
||||
import type {Agent, MonitorTask, MonitorTaskRequest} from '../../types';
|
||||
import {createMonitor, deleteMonitor, listMonitors, updateMonitor} from '../../api/monitor';
|
||||
import {getErrorMessage} from '../../lib/utils';
|
||||
import {getAgentPaging, getTags} from '@/api/agent.ts';
|
||||
import type {Agent, MonitorTask, MonitorTaskRequest} from '@/types';
|
||||
import {createMonitor, deleteMonitor, listMonitors, updateMonitor} from '@/api/monitor.ts';
|
||||
import {getErrorMessage} from '@/lib/utils';
|
||||
|
||||
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
} from 'lucide-react';
|
||||
import type {TooltipProps} from 'recharts';
|
||||
import {Area, AreaChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis,} from 'recharts';
|
||||
import {type AggregatedMonitorMetric, getMonitorHistory, getMonitorStatsById} from '../../api/monitor';
|
||||
import type {MonitorStats} from '../../types';
|
||||
import {type AggregatedMonitorMetric, getMonitorHistory, getMonitorStatsById} from '@/api/monitor.ts';
|
||||
import type {MonitorStats} from '@/types';
|
||||
|
||||
const formatTime = (ms: number): string => {
|
||||
if (!ms || ms <= 0) return '0 ms';
|
||||
|
||||
@@ -2,8 +2,8 @@ import {useEffect} from 'react';
|
||||
import {useNavigate} from 'react-router-dom';
|
||||
import {useQuery} from '@tanstack/react-query';
|
||||
import {AlertCircle, CheckCircle2, Clock, Loader2, Shield} from 'lucide-react';
|
||||
import {getPublicMonitors} from '../../api/monitor';
|
||||
import type {PublicMonitor} from '../../types';
|
||||
import {getPublicMonitors} from '@/api/monitor.ts';
|
||||
import type {PublicMonitor} from '@/types';
|
||||
import {usePublicLayout} from '../PublicLayout';
|
||||
|
||||
const formatTime = (ms: number): string => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, {type ReactNode, useEffect, useMemo, useState} from 'react';
|
||||
import {useNavigate, useParams} from 'react-router-dom';
|
||||
import {
|
||||
Activity,
|
||||
ArrowLeft,
|
||||
Cpu,
|
||||
HardDrive,
|
||||
@@ -33,19 +32,18 @@ import {
|
||||
type GetAgentMetricsRequest,
|
||||
getNetworkMetricsByInterface,
|
||||
type NetworkMetricByInterface
|
||||
} from '../../api/agent';
|
||||
} from '@/api/agent.ts';
|
||||
import type {
|
||||
Agent,
|
||||
AggregatedCPUMetric,
|
||||
AggregatedDiskIOMetric,
|
||||
AggregatedDiskMetric,
|
||||
AggregatedGPUMetric,
|
||||
AggregatedMemoryMetric,
|
||||
AggregatedNetworkConnectionMetric,
|
||||
AggregatedNetworkMetric,
|
||||
AggregatedTemperatureMetric,
|
||||
LatestMetrics
|
||||
} from '../../types';
|
||||
} from '@/types';
|
||||
|
||||
const formatBytes = (bytes: number | undefined | null): string => {
|
||||
if (!bytes || bytes <= 0) return '0 B';
|
||||
@@ -256,7 +254,7 @@ type MetricsState = {
|
||||
memory: AggregatedMemoryMetric[];
|
||||
network: AggregatedNetworkMetric[];
|
||||
networkConnection: AggregatedNetworkConnectionMetric[];
|
||||
disk: AggregatedDiskMetric[];
|
||||
// disk: AggregatedDiskMetric[];
|
||||
diskIO: AggregatedDiskIOMetric[];
|
||||
gpu: AggregatedGPUMetric[];
|
||||
temperature: AggregatedTemperatureMetric[];
|
||||
@@ -267,7 +265,7 @@ const createEmptyMetricsState = (): MetricsState => ({
|
||||
memory: [],
|
||||
network: [],
|
||||
networkConnection: [],
|
||||
disk: [],
|
||||
// disk: [],
|
||||
diskIO: [],
|
||||
gpu: [],
|
||||
temperature: [],
|
||||
@@ -278,7 +276,7 @@ const metricRequestConfig: Array<{ key: keyof MetricsState; type: GetAgentMetric
|
||||
{key: 'memory', type: 'memory'},
|
||||
{key: 'network', type: 'network'},
|
||||
{key: 'networkConnection', type: 'network_connection'},
|
||||
{key: 'disk', type: 'disk'},
|
||||
// {key: 'disk', type: 'disk'},
|
||||
{key: 'diskIO', type: 'disk_io'},
|
||||
{key: 'gpu', type: 'gpu'},
|
||||
{key: 'temperature', type: 'temperature'},
|
||||
|
||||
@@ -2,8 +2,8 @@ import {type ReactNode, useEffect, useState} from 'react';
|
||||
import {Link, useNavigate} from 'react-router-dom';
|
||||
import {useQuery} from '@tanstack/react-query';
|
||||
import {Cpu, EthernetPortIcon, HardDrive, Loader2, MemoryStick, Network} from 'lucide-react';
|
||||
import {listAgents, getPublicTags} from '../../api/agent';
|
||||
import type {Agent, LatestMetrics} from '../../types';
|
||||
import {listAgents, getPublicTags} from '@/api/agent.ts';
|
||||
import type {Agent, LatestMetrics} from '@/types';
|
||||
import {usePublicLayout} from '../PublicLayout';
|
||||
|
||||
interface AgentWithMetrics extends Agent {
|
||||
|
||||
@@ -2,9 +2,9 @@ import {useEffect} from 'react';
|
||||
import {App, Button, Card, Form, InputNumber, Space, Switch} from 'antd';
|
||||
import {Save} from 'lucide-react';
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||
import type {AlertConfig} from '../../types';
|
||||
import {createAlertConfig, getAlertConfigsByAgent, updateAlertConfig} from '../../api/alert';
|
||||
import {getErrorMessage} from '../../lib/utils';
|
||||
import type {AlertConfig} from '@/types';
|
||||
import {createAlertConfig, getAlertConfigsByAgent, updateAlertConfig} from '@/api/alert.ts';
|
||||
import {getErrorMessage} from '@/lib/utils';
|
||||
|
||||
const AlertSettings = () => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
@@ -2,9 +2,9 @@ import {useEffect} from 'react';
|
||||
import {App, Button, Card, Form, InputNumber, Space, Spin} from 'antd';
|
||||
import {Database, Clock, BarChart3} from 'lucide-react';
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||
import type {MetricsConfig} from '../../api/metrics-config';
|
||||
import {getMetricsConfig, saveMetricsConfig} from '../../api/metrics-config';
|
||||
import {getErrorMessage} from '../../lib/utils';
|
||||
import type {MetricsConfig} from '@/api/property.ts';
|
||||
import {getMetricsConfig, saveMetricsConfig} from '@/api/property.ts';
|
||||
import {getErrorMessage} from '@/lib/utils';
|
||||
|
||||
const MetricsConfigComponent = () => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
@@ -2,13 +2,13 @@ import {useEffect} from 'react';
|
||||
import {App, Button, Card, Collapse, Form, Input, Select, Space, Spin, Switch} from 'antd';
|
||||
import {TestTube} from 'lucide-react';
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||
import type {NotificationChannel} from '../../types';
|
||||
import {
|
||||
getNotificationChannels,
|
||||
type NotificationChannel,
|
||||
saveNotificationChannels,
|
||||
testNotificationChannel,
|
||||
} from '../../api/notification-channel';
|
||||
import {getErrorMessage} from '../../lib/utils';
|
||||
} from '@/api/property.ts';
|
||||
import {getErrorMessage} from '@/lib/utils';
|
||||
|
||||
const NotificationChannels = () => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
@@ -2,9 +2,9 @@ import {useEffect, useState} from 'react';
|
||||
import {App, Button, Card, Form, Input, Radio, Space, Spin, Upload} from 'antd';
|
||||
import {Upload as UploadIcon, Grid3x3, List} from 'lucide-react';
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||
import type {SystemConfig} from '../../api/system-config';
|
||||
import {getSystemConfig, saveSystemConfig} from '../../api/system-config';
|
||||
import {getErrorMessage} from '../../lib/utils';
|
||||
import type {SystemConfig} from '@/api/property.ts';
|
||||
import {getSystemConfig, saveSystemConfig} from '@/api/property.ts';
|
||||
import {getErrorMessage} from '@/lib/utils.ts';
|
||||
import type {RcFile} from 'antd/es/upload/interface';
|
||||
|
||||
const SystemConfigComponent = () => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {Tabs} from 'antd';
|
||||
import {Bell, MessageSquare, Settings2, Database} from 'lucide-react';
|
||||
import {Bell, Database, MessageSquare, Settings2} from 'lucide-react';
|
||||
import AlertSettings from './AlertSettings';
|
||||
import NotificationChannels from './NotificationChannels';
|
||||
import SystemConfig from './SystemConfig';
|
||||
import MetricsConfig from './MetricsConfig';
|
||||
import {PageHeader} from "../../components";
|
||||
import {PageHeader} from "@/components";
|
||||
|
||||
const Settings = () => {
|
||||
const items = [
|
||||
|
||||
@@ -443,45 +443,6 @@ export interface AlertConfig {
|
||||
updatedAt?: number;
|
||||
}
|
||||
|
||||
// 通知渠道配置(通过 type 标识,不再使用独立ID)
|
||||
export interface NotificationChannel {
|
||||
type: 'dingtalk' | 'wecom' | 'feishu' | 'email' | 'webhook'; // 渠道类型,作为唯一标识
|
||||
enabled: boolean; // 是否启用
|
||||
config: Record<string, any>; // JSON配置,根据type不同而不同
|
||||
}
|
||||
|
||||
// 各种通知渠道的配置类型
|
||||
export interface DingTalkConfig {
|
||||
secretKey: string; // 访问令牌
|
||||
signSecret?: string; // 加签密钥(可选)
|
||||
}
|
||||
|
||||
export interface WeComConfig {
|
||||
secretKey: string; // Webhook Key
|
||||
}
|
||||
|
||||
export interface FeishuConfig {
|
||||
secretKey: string; // Webhook Token
|
||||
signSecret?: string; // 签名密钥(可选)
|
||||
}
|
||||
|
||||
export interface WebhookConfig {
|
||||
url: string; // 自定义URL
|
||||
method?: string; // HTTP方法:GET, POST, PUT, PATCH, DELETE,默认POST
|
||||
headers?: Record<string, string>; // 自定义请求头
|
||||
bodyTemplate?: 'json' | 'form' | 'custom'; // 请求体模板类型
|
||||
customBody?: string; // 自定义请求体模板(当 bodyTemplate 为 custom 时使用)
|
||||
}
|
||||
|
||||
export interface EmailConfig {
|
||||
smtpHost: string;
|
||||
smtpPort: number;
|
||||
username: string;
|
||||
password: string;
|
||||
from: string;
|
||||
to: string[];
|
||||
}
|
||||
|
||||
export interface AlertRecord {
|
||||
id: number;
|
||||
agentId: string;
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
+15
-3
@@ -1,7 +1,19 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import {defineConfig} from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import path from "path";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
@@ -8,6 +9,11 @@ export default defineConfig({
|
||||
react(),
|
||||
tailwindcss(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api/': {
|
||||
|
||||
Reference in new issue
Block a user