- 区分服务端和客户端版本,避免频繁更新客户端
- 优化前端代码
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 指定)
|
# 版本号(可通过 make VERSION=1.0.0 指定)
|
||||||
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
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)
|
GIT_REVISION=$(shell git rev-parse HEAD)
|
||||||
GO_VERSION=$(shell go version)
|
GO_VERSION=$(shell go version)
|
||||||
BUILD_TIME=$(shell date +%Y-%m-%d_%H:%M:%S)
|
BUILD_TIME=$(shell date +%Y-%m-%d_%H:%M:%S)
|
||||||
|
|
||||||
# Go 构建参数
|
# Go 构建参数
|
||||||
LDFLAGS=-s -w -X 'github.com/dushixiang/pika/pkg/version.Version=$(VERSION)'
|
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
|
GOFLAGS=CGO_ENABLED=0
|
||||||
|
|
||||||
# 构建前端
|
# 构建前端
|
||||||
@@ -26,21 +40,23 @@ build-servers:
|
|||||||
|
|
||||||
# 构建所有平台的 Agent
|
# 构建所有平台的 Agent
|
||||||
build-agents:
|
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
|
@mkdir -p bin/agents
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
$(GOFLAGS) GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-linux-amd64 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="$(LDFLAGS)" -o bin/agents/pika-agent-linux-arm64 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="$(LDFLAGS)" -o bin/agents/pika-agent-linux-armv7 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="$(LDFLAGS)" -o bin/agents/pika-agent-linux-loong64 cmd/agent/*.go
|
$(GOFLAGS) GOOS=linux GOARCH=loong64 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-linux-loong64 cmd/agent/*.go
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
$(GOFLAGS) GOOS=darwin GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o bin/agents/pika-agent-darwin-amd64 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="$(LDFLAGS)" -o bin/agents/pika-agent-darwin-arm64 cmd/agent/*.go
|
$(GOFLAGS) GOOS=darwin GOARCH=arm64 go build -ldflags="$(AGENT_LDFLAGS)" -o bin/agents/pika-agent-darwin-arm64 cmd/agent/*.go
|
||||||
|
|
||||||
# Windows
|
# 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 "All agents built successfully!"
|
||||||
@echo "Compressing agents with UPX..."
|
@echo "Compressing agents with UPX..."
|
||||||
@@ -80,7 +96,8 @@ wire:
|
|||||||
|
|
||||||
# 显示版本信息
|
# 显示版本信息
|
||||||
version:
|
version:
|
||||||
@echo "Version: $(VERSION)"
|
@echo "Server Version: $(VERSION)"
|
||||||
|
@echo "Agent Version: $(AGENT_VERSION)"
|
||||||
@echo "Git Revision: $(GIT_REVISION)"
|
@echo "Git Revision: $(GIT_REVISION)"
|
||||||
@echo "Go Version: $(GO_VERSION)"
|
@echo "Go Version: $(GO_VERSION)"
|
||||||
@echo "Build Time: $(BUILD_TIME)"
|
@echo "Build Time: $(BUILD_TIME)"
|
||||||
@@ -552,7 +552,7 @@ func (h *AgentHandler) GetAgents(c echo.Context) error {
|
|||||||
// GetAgentVersion 获取 Agent 版本信息
|
// GetAgentVersion 获取 Agent 版本信息
|
||||||
func (h *AgentHandler) GetAgentVersion(c echo.Context) error {
|
func (h *AgentHandler) GetAgentVersion(c echo.Context) error {
|
||||||
return orz.Ok(c, orz.Map{
|
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 {
|
func GetVersion() string {
|
||||||
return version.GetVersion()
|
return version.GetAgentVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleTamperProtect 处理防篡改保护指令(增量更新)
|
// handleTamperProtect 处理防篡改保护指令(增量更新)
|
||||||
|
|||||||
+13
-2
@@ -1,12 +1,23 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
// Version 版本号(通过 -ldflags 注入)
|
// Version 服务端版本号(通过 -ldflags 注入)
|
||||||
var Version = "dev"
|
var Version = "dev"
|
||||||
|
|
||||||
// GetVersion 获取版本号
|
// AgentVersion Agent 版本号(通过 -ldflags 注入)
|
||||||
|
var AgentVersion = "dev"
|
||||||
|
|
||||||
|
// GetVersion 获取服务端版本号
|
||||||
func GetVersion() string {
|
func GetVersion() string {
|
||||||
if Version == "" {
|
if Version == "" {
|
||||||
return "dev"
|
return "dev"
|
||||||
}
|
}
|
||||||
return Version
|
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 React from 'react';
|
||||||
import {Button, Space} from 'antd';
|
import {Button} from 'antd';
|
||||||
import type {LucideIcon} from 'lucide-react';
|
import type {LucideIcon} from 'lucide-react';
|
||||||
|
|
||||||
export interface Action {
|
export interface Action {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import {Outlet, useLocation, useNavigate} from 'react-router-dom';
|
|||||||
import type {MenuProps} from 'antd';
|
import type {MenuProps} from 'antd';
|
||||||
import {App, Avatar, Button, Dropdown, Space} 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 {Activity, BookOpen, Eye, Key, LogOut, Server, Settings, User as UserIcon} from 'lucide-react';
|
||||||
import {logout} from '../../api/auth';
|
import {logout} from '@/api/auth.ts';
|
||||||
import {getServerVersion} from '../../api/agent';
|
import {getServerVersion} from '@/api/agent.ts';
|
||||||
import type {User} from '../../types';
|
import type {User} from '@/types';
|
||||||
import {cn} from '../../lib/utils';
|
import {cn} from '@/lib/utils';
|
||||||
|
|
||||||
interface NavItem {
|
interface NavItem {
|
||||||
key: string;
|
key: string;
|
||||||
|
|||||||
@@ -1,30 +1,13 @@
|
|||||||
import {useEffect, useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {useNavigate, useParams, useSearchParams} from 'react-router-dom';
|
import {useNavigate, useParams, useSearchParams} from 'react-router-dom';
|
||||||
import type {MenuProps, TabsProps} from 'antd';
|
import type {MenuProps, TabsProps} from 'antd';
|
||||||
import {App, Button, Card, Col, Descriptions, Dropdown, Row, Space, Spin, Tag, Tabs, Alert, Statistic} from 'antd';
|
import {Alert, App, Button, Card, Descriptions, Dropdown, Space, Spin, Tabs, Tag} from 'antd';
|
||||||
import {
|
import {Activity, ArrowLeft, Clock, FileWarning, RefreshCw, Shield, Terminal} from 'lucide-react';
|
||||||
Activity,
|
|
||||||
ArrowLeft,
|
|
||||||
Clock,
|
|
||||||
RefreshCw,
|
|
||||||
Shield,
|
|
||||||
Terminal,
|
|
||||||
CheckCircle,
|
|
||||||
XCircle,
|
|
||||||
AlertTriangle,
|
|
||||||
TrendingUp,
|
|
||||||
FileWarning
|
|
||||||
} from 'lucide-react';
|
|
||||||
import TamperProtection from './TamperProtection.tsx';
|
import TamperProtection from './TamperProtection.tsx';
|
||||||
import {
|
import {getAgentForAdmin, getAuditResult, sendAuditCommand, type VPSAuditResult} from '@/api/agent.ts';
|
||||||
getAgentForAdmin,
|
import type {Agent} from '@/types';
|
||||||
getAuditResult,
|
|
||||||
sendAuditCommand,
|
|
||||||
type VPSAuditResult
|
|
||||||
} from '../../api/agent';
|
|
||||||
import type {Agent} from '../../types';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import {getErrorMessage} from '../../lib/utils';
|
import {getErrorMessage} from '@/lib/utils';
|
||||||
import AuditResultView from './AuditResultView';
|
import AuditResultView from './AuditResultView';
|
||||||
|
|
||||||
const AgentDetail = () => {
|
const AgentDetail = () => {
|
||||||
@@ -235,7 +218,7 @@ const AgentDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
children: agent?.os.toLowerCase().includes('linux') ? (
|
children: agent?.os.toLowerCase().includes('linux') ? (
|
||||||
<TamperProtection agentId={agent.id} />
|
<TamperProtection agentId={agent.id}/>
|
||||||
) : (
|
) : (
|
||||||
<Alert
|
<Alert
|
||||||
message="功能限制"
|
message="功能限制"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, {type ReactElement, useCallback, useEffect, useMemo, useState} from 'react';
|
import React, {type ReactElement, useCallback, useEffect, useMemo, useState} from 'react';
|
||||||
import {Alert, App, Button, Card, Select, Space, Tabs, Typography} from 'antd';
|
import {Alert, App, Button, Card, Select, Space, Tabs, Typography} from 'antd';
|
||||||
import {CopyIcon} from 'lucide-react';
|
import {CopyIcon} from 'lucide-react';
|
||||||
import {listApiKeys} from '../../api/apiKey';
|
import {listApiKeys} from '@/api/apiKey.ts';
|
||||||
import type {ApiKey} from '../../types';
|
import type {ApiKey} from '@/types';
|
||||||
import linuxPng from '../../assets/os/linux.png';
|
import linuxPng from '../../assets/os/linux.png';
|
||||||
import applePng from '../../assets/os/apple.png';
|
import applePng from '../../assets/os/apple.png';
|
||||||
import windowsPng from '../../assets/os/win11.png';
|
import windowsPng from '../../assets/os/win11.png';
|
||||||
@@ -377,7 +377,7 @@ curl -L ${serverUrl}${config.downloadUrl} -o ${AGENT_NAME}`
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
style={{ marginTop: 24 }}
|
style={{marginTop: 24}}
|
||||||
tabPosition="left"
|
tabPosition="left"
|
||||||
items={tabItems}
|
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 {App, Button, DatePicker, Divider, Dropdown, Form, Input, Modal, Select, Space, Tag} from 'antd';
|
||||||
import type {MenuProps} from 'antd';
|
import type {MenuProps} from 'antd';
|
||||||
import {Edit, Eye, RefreshCw, Plus, Shield, Trash2, MoreVertical} from 'lucide-react';
|
import {Edit, Eye, RefreshCw, Plus, Shield, Trash2, MoreVertical} from 'lucide-react';
|
||||||
import {deleteAgent, getAgentPaging, getTags, updateAgentInfo} from '../../api/agent';
|
import {deleteAgent, getAgentPaging, getTags, updateAgentInfo} from '@/api/agent.ts';
|
||||||
import type {Agent} from '../../types';
|
import type {Agent} from '@/types';
|
||||||
import {getErrorMessage} from '../../lib/utils';
|
import {getErrorMessage} from '@/lib/utils';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import {PageHeader} from '../../components';
|
import {PageHeader} from '@/components';
|
||||||
|
|
||||||
const AgentList = () => {
|
const AgentList = () => {
|
||||||
const navigate = useNavigate();
|
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 {
|
import {
|
||||||
CheckCircle,
|
|
||||||
XCircle,
|
|
||||||
AlertTriangle,
|
|
||||||
MinusCircle,
|
|
||||||
Server,
|
|
||||||
Network,
|
|
||||||
Cpu,
|
|
||||||
Users,
|
|
||||||
FileText,
|
|
||||||
Shield,
|
|
||||||
Activity,
|
Activity,
|
||||||
Calendar,
|
|
||||||
Settings,
|
|
||||||
PlayCircle,
|
|
||||||
AlertOctagon,
|
AlertOctagon,
|
||||||
LogIn
|
AlertTriangle,
|
||||||
|
Calendar,
|
||||||
|
CheckCircle,
|
||||||
|
Cpu,
|
||||||
|
FileText,
|
||||||
|
LogIn,
|
||||||
|
MinusCircle,
|
||||||
|
Network,
|
||||||
|
PlayCircle,
|
||||||
|
Server,
|
||||||
|
Settings,
|
||||||
|
Shield,
|
||||||
|
Users,
|
||||||
|
XCircle
|
||||||
} from 'lucide-react';
|
} 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 dayjs from 'dayjs';
|
||||||
import duration from 'dayjs/plugin/duration';
|
import duration from 'dayjs/plugin/duration';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
dayjs.extend(duration);
|
dayjs.extend(duration);
|
||||||
|
|
||||||
const {Panel} = Collapse;
|
|
||||||
|
|
||||||
interface RiskItem {
|
interface RiskItem {
|
||||||
level: 'critical' | 'high' | 'medium' | 'low';
|
level: 'critical' | 'high' | 'medium' | 'low';
|
||||||
title: string;
|
title: string;
|
||||||
@@ -472,7 +470,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
|||||||
{/* 风险概览 */}
|
{/* 风险概览 */}
|
||||||
{risks.length > 0 && (
|
{risks.length > 0 && (
|
||||||
<Card
|
<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"
|
className="border-red-200 bg-red-50"
|
||||||
>
|
>
|
||||||
<List
|
<List
|
||||||
@@ -592,7 +591,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
|||||||
showIcon
|
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 ? (
|
{result.assetInventory.processAssets?.suspiciousProcesses?.length ? (
|
||||||
<Table
|
<Table
|
||||||
size="small"
|
size="small"
|
||||||
@@ -602,7 +602,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
|||||||
{
|
{
|
||||||
title: '异常信息',
|
title: '异常信息',
|
||||||
key: 'issue',
|
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"
|
rowKey="pid"
|
||||||
@@ -767,15 +768,26 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
|||||||
showIcon
|
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 ? (
|
{result.assetInventory.fileAssets?.tmpExecutables?.length ? (
|
||||||
<Table
|
<Table
|
||||||
size="small"
|
size="small"
|
||||||
dataSource={result.assetInventory.fileAssets.tmpExecutables}
|
dataSource={result.assetInventory.fileAssets.tmpExecutables}
|
||||||
columns={[
|
columns={[
|
||||||
{title: '路径', dataIndex: 'path', key: 'path'},
|
{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'},
|
{title: '权限', dataIndex: 'permissions', key: 'permissions'},
|
||||||
]}
|
]}
|
||||||
rowKey="path"
|
rowKey="path"
|
||||||
@@ -825,7 +837,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
|||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Empty description={showAllServices ? "无Systemd服务" : "无活跃的Systemd服务"}/>
|
<Empty
|
||||||
|
description={showAllServices ? "无Systemd服务" : "无活跃的Systemd服务"}/>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
<Card size="small" title={<Space><PlayCircle size={16}/>启动脚本</Space>}>
|
<Card size="small" title={<Space><PlayCircle size={16}/>启动脚本</Space>}>
|
||||||
@@ -870,7 +883,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => {
|
|||||||
<Empty description="无登录历史"/>
|
<Empty description="无登录历史"/>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</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 ? (
|
{result.assetInventory.loginAssets?.failedLogins?.length ? (
|
||||||
<Table
|
<Table
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
type TamperConfig,
|
type TamperConfig,
|
||||||
type TamperEvent,
|
type TamperEvent,
|
||||||
updateTamperConfig
|
updateTamperConfig
|
||||||
} from '../../api/tamper.ts';
|
} from '@/api/tamper.ts';
|
||||||
import {App} from "antd";
|
import {App} from "antd";
|
||||||
|
|
||||||
interface TamperProtectionProps {
|
interface TamperProtectionProps {
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import {
|
|||||||
generateApiKey,
|
generateApiKey,
|
||||||
listApiKeys,
|
listApiKeys,
|
||||||
updateApiKeyName,
|
updateApiKeyName,
|
||||||
} from '../../api/apiKey';
|
} from '@/api/apiKey.ts';
|
||||||
import type {ApiKey, GenerateApiKeyRequest} from '../../types';
|
import type {ApiKey, GenerateApiKeyRequest} from '@/types';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import {getErrorMessage} from '../../lib/utils';
|
import {getErrorMessage} from '@/lib/utils';
|
||||||
import {PageHeader} from '../../components';
|
import {PageHeader} from '@/components';
|
||||||
|
|
||||||
const ApiKeyList = () => {
|
const ApiKeyList = () => {
|
||||||
const {message: messageApi} = App.useApp();
|
const {message: messageApi} = App.useApp();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {useEffect} from 'react';
|
import {useEffect} from 'react';
|
||||||
import {useNavigate, useSearchParams} from 'react-router-dom';
|
import {useNavigate, useSearchParams} from 'react-router-dom';
|
||||||
import {App} from 'antd';
|
import {App} from 'antd';
|
||||||
import {githubLogin} from '../../api/auth';
|
import {githubLogin} from '@/api/auth.ts';
|
||||||
|
|
||||||
const GitHubCallback = () => {
|
const GitHubCallback = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {useEffect} from 'react';
|
import {useEffect} from 'react';
|
||||||
import {useNavigate, useSearchParams} from 'react-router-dom';
|
import {useNavigate, useSearchParams} from 'react-router-dom';
|
||||||
import {App} from 'antd';
|
import {App} from 'antd';
|
||||||
import {oidcLogin} from '../../api/auth';
|
import {oidcLogin} from '@/api/auth.ts';
|
||||||
|
|
||||||
const OIDCCallback = () => {
|
const OIDCCallback = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import {useEffect, useState} from 'react';
|
|||||||
import {useNavigate} from 'react-router-dom';
|
import {useNavigate} from 'react-router-dom';
|
||||||
import {App, Button, Form, Input} from 'antd';
|
import {App, Button, Form, Input} from 'antd';
|
||||||
import {GithubOutlined, GlobalOutlined, LockOutlined, UserOutlined} from '@ant-design/icons';
|
import {GithubOutlined, GlobalOutlined, LockOutlined, UserOutlined} from '@ant-design/icons';
|
||||||
// 移除不需要的图标组件,保持轻量
|
import {getAuthConfig, getGitHubAuthURL, getOIDCAuthURL, login} from '@/api/auth.ts';
|
||||||
import {getAuthConfig, getGitHubAuthURL, getOIDCAuthURL, login} from '../../api/auth';
|
import type {LoginRequest} from '@/types';
|
||||||
import type {LoginRequest} from '../../types';
|
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const [loading, setLoading] = useState(false);
|
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 type {ActionType, ProColumns} from '@ant-design/pro-components';
|
||||||
import {ProTable} 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 {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 {Edit, MinusCircle, Plus, PlusCircle, RefreshCw, Trash2} from 'lucide-react';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import {getAgentPaging, getTags} from '../../api/agent';
|
import {getAgentPaging, getTags} from '@/api/agent.ts';
|
||||||
import type {Agent, MonitorTask, MonitorTaskRequest} from '../../types';
|
import type {Agent, MonitorTask, MonitorTaskRequest} from '@/types';
|
||||||
import {createMonitor, deleteMonitor, listMonitors, updateMonitor} from '../../api/monitor';
|
import {createMonitor, deleteMonitor, listMonitors, updateMonitor} from '@/api/monitor.ts';
|
||||||
import {getErrorMessage} from '../../lib/utils';
|
import {getErrorMessage} from '@/lib/utils';
|
||||||
|
|
||||||
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import type {TooltipProps} from 'recharts';
|
import type {TooltipProps} from 'recharts';
|
||||||
import {Area, AreaChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis,} from 'recharts';
|
import {Area, AreaChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis,} from 'recharts';
|
||||||
import {type AggregatedMonitorMetric, getMonitorHistory, getMonitorStatsById} from '../../api/monitor';
|
import {type AggregatedMonitorMetric, getMonitorHistory, getMonitorStatsById} from '@/api/monitor.ts';
|
||||||
import type {MonitorStats} from '../../types';
|
import type {MonitorStats} from '@/types';
|
||||||
|
|
||||||
const formatTime = (ms: number): string => {
|
const formatTime = (ms: number): string => {
|
||||||
if (!ms || ms <= 0) return '0 ms';
|
if (!ms || ms <= 0) return '0 ms';
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import {useEffect} from 'react';
|
|||||||
import {useNavigate} from 'react-router-dom';
|
import {useNavigate} from 'react-router-dom';
|
||||||
import {useQuery} from '@tanstack/react-query';
|
import {useQuery} from '@tanstack/react-query';
|
||||||
import {AlertCircle, CheckCircle2, Clock, Loader2, Shield} from 'lucide-react';
|
import {AlertCircle, CheckCircle2, Clock, Loader2, Shield} from 'lucide-react';
|
||||||
import {getPublicMonitors} from '../../api/monitor';
|
import {getPublicMonitors} from '@/api/monitor.ts';
|
||||||
import type {PublicMonitor} from '../../types';
|
import type {PublicMonitor} from '@/types';
|
||||||
import {usePublicLayout} from '../PublicLayout';
|
import {usePublicLayout} from '../PublicLayout';
|
||||||
|
|
||||||
const formatTime = (ms: number): string => {
|
const formatTime = (ms: number): string => {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, {type ReactNode, useEffect, useMemo, useState} from 'react';
|
import React, {type ReactNode, useEffect, useMemo, useState} from 'react';
|
||||||
import {useNavigate, useParams} from 'react-router-dom';
|
import {useNavigate, useParams} from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Activity,
|
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
Cpu,
|
Cpu,
|
||||||
HardDrive,
|
HardDrive,
|
||||||
@@ -33,19 +32,18 @@ import {
|
|||||||
type GetAgentMetricsRequest,
|
type GetAgentMetricsRequest,
|
||||||
getNetworkMetricsByInterface,
|
getNetworkMetricsByInterface,
|
||||||
type NetworkMetricByInterface
|
type NetworkMetricByInterface
|
||||||
} from '../../api/agent';
|
} from '@/api/agent.ts';
|
||||||
import type {
|
import type {
|
||||||
Agent,
|
Agent,
|
||||||
AggregatedCPUMetric,
|
AggregatedCPUMetric,
|
||||||
AggregatedDiskIOMetric,
|
AggregatedDiskIOMetric,
|
||||||
AggregatedDiskMetric,
|
|
||||||
AggregatedGPUMetric,
|
AggregatedGPUMetric,
|
||||||
AggregatedMemoryMetric,
|
AggregatedMemoryMetric,
|
||||||
AggregatedNetworkConnectionMetric,
|
AggregatedNetworkConnectionMetric,
|
||||||
AggregatedNetworkMetric,
|
AggregatedNetworkMetric,
|
||||||
AggregatedTemperatureMetric,
|
AggregatedTemperatureMetric,
|
||||||
LatestMetrics
|
LatestMetrics
|
||||||
} from '../../types';
|
} from '@/types';
|
||||||
|
|
||||||
const formatBytes = (bytes: number | undefined | null): string => {
|
const formatBytes = (bytes: number | undefined | null): string => {
|
||||||
if (!bytes || bytes <= 0) return '0 B';
|
if (!bytes || bytes <= 0) return '0 B';
|
||||||
@@ -256,7 +254,7 @@ type MetricsState = {
|
|||||||
memory: AggregatedMemoryMetric[];
|
memory: AggregatedMemoryMetric[];
|
||||||
network: AggregatedNetworkMetric[];
|
network: AggregatedNetworkMetric[];
|
||||||
networkConnection: AggregatedNetworkConnectionMetric[];
|
networkConnection: AggregatedNetworkConnectionMetric[];
|
||||||
disk: AggregatedDiskMetric[];
|
// disk: AggregatedDiskMetric[];
|
||||||
diskIO: AggregatedDiskIOMetric[];
|
diskIO: AggregatedDiskIOMetric[];
|
||||||
gpu: AggregatedGPUMetric[];
|
gpu: AggregatedGPUMetric[];
|
||||||
temperature: AggregatedTemperatureMetric[];
|
temperature: AggregatedTemperatureMetric[];
|
||||||
@@ -267,7 +265,7 @@ const createEmptyMetricsState = (): MetricsState => ({
|
|||||||
memory: [],
|
memory: [],
|
||||||
network: [],
|
network: [],
|
||||||
networkConnection: [],
|
networkConnection: [],
|
||||||
disk: [],
|
// disk: [],
|
||||||
diskIO: [],
|
diskIO: [],
|
||||||
gpu: [],
|
gpu: [],
|
||||||
temperature: [],
|
temperature: [],
|
||||||
@@ -278,7 +276,7 @@ const metricRequestConfig: Array<{ key: keyof MetricsState; type: GetAgentMetric
|
|||||||
{key: 'memory', type: 'memory'},
|
{key: 'memory', type: 'memory'},
|
||||||
{key: 'network', type: 'network'},
|
{key: 'network', type: 'network'},
|
||||||
{key: 'networkConnection', type: 'network_connection'},
|
{key: 'networkConnection', type: 'network_connection'},
|
||||||
{key: 'disk', type: 'disk'},
|
// {key: 'disk', type: 'disk'},
|
||||||
{key: 'diskIO', type: 'disk_io'},
|
{key: 'diskIO', type: 'disk_io'},
|
||||||
{key: 'gpu', type: 'gpu'},
|
{key: 'gpu', type: 'gpu'},
|
||||||
{key: 'temperature', type: 'temperature'},
|
{key: 'temperature', type: 'temperature'},
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import {type ReactNode, useEffect, useState} from 'react';
|
|||||||
import {Link, useNavigate} from 'react-router-dom';
|
import {Link, useNavigate} from 'react-router-dom';
|
||||||
import {useQuery} from '@tanstack/react-query';
|
import {useQuery} from '@tanstack/react-query';
|
||||||
import {Cpu, EthernetPortIcon, HardDrive, Loader2, MemoryStick, Network} from 'lucide-react';
|
import {Cpu, EthernetPortIcon, HardDrive, Loader2, MemoryStick, Network} from 'lucide-react';
|
||||||
import {listAgents, getPublicTags} from '../../api/agent';
|
import {listAgents, getPublicTags} from '@/api/agent.ts';
|
||||||
import type {Agent, LatestMetrics} from '../../types';
|
import type {Agent, LatestMetrics} from '@/types';
|
||||||
import {usePublicLayout} from '../PublicLayout';
|
import {usePublicLayout} from '../PublicLayout';
|
||||||
|
|
||||||
interface AgentWithMetrics extends Agent {
|
interface AgentWithMetrics extends Agent {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import {useEffect} from 'react';
|
|||||||
import {App, Button, Card, Form, InputNumber, Space, Switch} from 'antd';
|
import {App, Button, Card, Form, InputNumber, Space, Switch} from 'antd';
|
||||||
import {Save} from 'lucide-react';
|
import {Save} from 'lucide-react';
|
||||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||||
import type {AlertConfig} from '../../types';
|
import type {AlertConfig} from '@/types';
|
||||||
import {createAlertConfig, getAlertConfigsByAgent, updateAlertConfig} from '../../api/alert';
|
import {createAlertConfig, getAlertConfigsByAgent, updateAlertConfig} from '@/api/alert.ts';
|
||||||
import {getErrorMessage} from '../../lib/utils';
|
import {getErrorMessage} from '@/lib/utils';
|
||||||
|
|
||||||
const AlertSettings = () => {
|
const AlertSettings = () => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import {useEffect} from 'react';
|
|||||||
import {App, Button, Card, Form, InputNumber, Space, Spin} from 'antd';
|
import {App, Button, Card, Form, InputNumber, Space, Spin} from 'antd';
|
||||||
import {Database, Clock, BarChart3} from 'lucide-react';
|
import {Database, Clock, BarChart3} from 'lucide-react';
|
||||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||||
import type {MetricsConfig} from '../../api/metrics-config';
|
import type {MetricsConfig} from '@/api/property.ts';
|
||||||
import {getMetricsConfig, saveMetricsConfig} from '../../api/metrics-config';
|
import {getMetricsConfig, saveMetricsConfig} from '@/api/property.ts';
|
||||||
import {getErrorMessage} from '../../lib/utils';
|
import {getErrorMessage} from '@/lib/utils';
|
||||||
|
|
||||||
const MetricsConfigComponent = () => {
|
const MetricsConfigComponent = () => {
|
||||||
const [form] = Form.useForm();
|
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 {App, Button, Card, Collapse, Form, Input, Select, Space, Spin, Switch} from 'antd';
|
||||||
import {TestTube} from 'lucide-react';
|
import {TestTube} from 'lucide-react';
|
||||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||||
import type {NotificationChannel} from '../../types';
|
|
||||||
import {
|
import {
|
||||||
getNotificationChannels,
|
getNotificationChannels,
|
||||||
|
type NotificationChannel,
|
||||||
saveNotificationChannels,
|
saveNotificationChannels,
|
||||||
testNotificationChannel,
|
testNotificationChannel,
|
||||||
} from '../../api/notification-channel';
|
} from '@/api/property.ts';
|
||||||
import {getErrorMessage} from '../../lib/utils';
|
import {getErrorMessage} from '@/lib/utils';
|
||||||
|
|
||||||
const NotificationChannels = () => {
|
const NotificationChannels = () => {
|
||||||
const [form] = Form.useForm();
|
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 {App, Button, Card, Form, Input, Radio, Space, Spin, Upload} from 'antd';
|
||||||
import {Upload as UploadIcon, Grid3x3, List} from 'lucide-react';
|
import {Upload as UploadIcon, Grid3x3, List} from 'lucide-react';
|
||||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||||
import type {SystemConfig} from '../../api/system-config';
|
import type {SystemConfig} from '@/api/property.ts';
|
||||||
import {getSystemConfig, saveSystemConfig} from '../../api/system-config';
|
import {getSystemConfig, saveSystemConfig} from '@/api/property.ts';
|
||||||
import {getErrorMessage} from '../../lib/utils';
|
import {getErrorMessage} from '@/lib/utils.ts';
|
||||||
import type {RcFile} from 'antd/es/upload/interface';
|
import type {RcFile} from 'antd/es/upload/interface';
|
||||||
|
|
||||||
const SystemConfigComponent = () => {
|
const SystemConfigComponent = () => {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {Tabs} from 'antd';
|
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 AlertSettings from './AlertSettings';
|
||||||
import NotificationChannels from './NotificationChannels';
|
import NotificationChannels from './NotificationChannels';
|
||||||
import SystemConfig from './SystemConfig';
|
import SystemConfig from './SystemConfig';
|
||||||
import MetricsConfig from './MetricsConfig';
|
import MetricsConfig from './MetricsConfig';
|
||||||
import {PageHeader} from "../../components";
|
import {PageHeader} from "@/components";
|
||||||
|
|
||||||
const Settings = () => {
|
const Settings = () => {
|
||||||
const items = [
|
const items = [
|
||||||
|
|||||||
@@ -443,45 +443,6 @@ export interface AlertConfig {
|
|||||||
updatedAt?: number;
|
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 {
|
export interface AlertRecord {
|
||||||
id: number;
|
id: number;
|
||||||
agentId: string;
|
agentId: string;
|
||||||
|
|||||||
@@ -15,6 +15,14 @@
|
|||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
+15
-3
@@ -1,7 +1,19 @@
|
|||||||
{
|
{
|
||||||
"files": [],
|
"files": [],
|
||||||
"references": [
|
"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 {defineConfig} from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -8,6 +9,11 @@ export default defineConfig({
|
|||||||
react(),
|
react(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
],
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api/': {
|
'/api/': {
|
||||||
|
|||||||
Reference in new issue
Block a user