diff --git a/Makefile b/Makefile index f6e3862..31c5332 100644 --- a/Makefile +++ b/Makefile @@ -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)" diff --git a/internal/handler/agent_handler.go b/internal/handler/agent_handler.go index f7b68d0..4fb0735 100644 --- a/internal/handler/agent_handler.go +++ b/internal/handler/agent_handler.go @@ -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(), }) } diff --git a/pkg/agent/service/agent.go b/pkg/agent/service/agent.go index b2aa294..05108c2 100644 --- a/pkg/agent/service/agent.go +++ b/pkg/agent/service/agent.go @@ -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 处理防篡改保护指令(增量更新) diff --git a/pkg/version/version.go b/pkg/version/version.go index 7ef48f5..3b5af16 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -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 +} diff --git a/web/src/api/notification-channel.ts b/web/src/api/notification-channel.ts deleted file mode 100644 index 8e6fd60..0000000 --- a/web/src/api/notification-channel.ts +++ /dev/null @@ -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 => { - 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 => { - 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; -}; diff --git a/web/src/api/property.ts b/web/src/api/property.ts new file mode 100644 index 0000000..1599576 --- /dev/null +++ b/web/src/api/property.ts @@ -0,0 +1,94 @@ +import { get, post, put } from './request'; + +// ==================== 通用 Property 接口 ==================== + +// 通用的 Property 响应类型 +export interface PropertyResponse { + id: string; + name: string; + value: T; +} + +// 通用的获取 Property 方法 +export const getProperty = async (propertyId: string): Promise => { + const response = await get>(`/admin/properties/${propertyId}`); + return response.data.value; +}; + +// 通用的保存 Property 方法 +export const saveProperty = async (propertyId: string, name: string, value: T): Promise => { + 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 => { + return getProperty(PROPERTY_ID_METRICS_CONFIG); +}; + +// 保存指标配置 +export const saveMetricsConfig = async (config: MetricsConfig): Promise => { + 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; // JSON配置,根据type不同而不同 +} + +// 获取通知渠道列表 +export const getNotificationChannels = async (): Promise => { + const channels = await getProperty(PROPERTY_ID_NOTIFICATION_CHANNELS); + return channels || []; +}; + +// 保存通知渠道列表 +export const saveNotificationChannels = async (channels: NotificationChannel[]): Promise => { + 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 => { + return getProperty(PROPERTY_ID_SYSTEM_CONFIG); +}; + +// 保存系统配置 +export const saveSystemConfig = async (config: SystemConfig): Promise => { + return saveProperty(PROPERTY_ID_SYSTEM_CONFIG, '系统配置', config); +}; + diff --git a/web/src/api/system-config.ts b/web/src/api/system-config.ts deleted file mode 100644 index 12e7e9a..0000000 --- a/web/src/api/system-config.ts +++ /dev/null @@ -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 => { - 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 => { - await put(`/admin/properties/${PROPERTY_ID_SYSTEM_CONFIG}`, { - name: '系统配置', - value: config, - }); -}; diff --git a/web/src/components/PageHeader.tsx b/web/src/components/PageHeader.tsx index e91246e..6590a16 100644 --- a/web/src/components/PageHeader.tsx +++ b/web/src/components/PageHeader.tsx @@ -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 { diff --git a/web/src/pages/AdminLayout/index.tsx b/web/src/pages/AdminLayout/index.tsx index 8183e5e..a7e4fe5 100644 --- a/web/src/pages/AdminLayout/index.tsx +++ b/web/src/pages/AdminLayout/index.tsx @@ -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; diff --git a/web/src/pages/Agents/AgentDetail.tsx b/web/src/pages/Agents/AgentDetail.tsx index 1faa6e6..c801581 100644 --- a/web/src/pages/Agents/AgentDetail.tsx +++ b/web/src/pages/Agents/AgentDetail.tsx @@ -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 = () => { ), children: agent?.os.toLowerCase().includes('linux') ? ( - + ) : ( diff --git a/web/src/pages/Agents/AgentList.tsx b/web/src/pages/Agents/AgentList.tsx index 74cb4e2..ff4046c 100644 --- a/web/src/pages/Agents/AgentList.tsx +++ b/web/src/pages/Agents/AgentList.tsx @@ -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(); diff --git a/web/src/pages/Agents/AuditResultView.tsx b/web/src/pages/Agents/AuditResultView.tsx index 7dfbc08..2cb14b1 100644 --- a/web/src/pages/Agents/AuditResultView.tsx +++ b/web/src/pages/Agents/AuditResultView.tsx @@ -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 && ( 风险概览} + title={风险概览} className="border-red-200 bg-red-50" > { showIcon /> )} - 可疑进程}> + 可疑进程}> {result.assetInventory.processAssets?.suspiciousProcesses?.length ? ( { { title: '异常信息', key: 'issue', - render: (record: ProcessInfo) => record.exeDeleted ? Exe已删除 : null + render: (record: ProcessInfo) => record.exeDeleted ? + Exe已删除 : null } ]} rowKey="pid" @@ -767,15 +768,26 @@ const AuditResultView = ({result}: AuditResultViewProps) => { showIcon /> )} - 临时目录可执行文件}> + 临时目录可执行文件}> {result.assetInventory.fileAssets?.tmpExecutables?.length ? (
`${(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} /> ) : ( - + )} 启动脚本}> @@ -870,7 +883,8 @@ const AuditResultView = ({result}: AuditResultViewProps) => { )} - 失败登录历史}> + 失败登录历史}> {result.assetInventory.loginAssets?.failedLogins?.length ? (
{ const {message: messageApi} = App.useApp(); diff --git a/web/src/pages/Login/GitHubCallback.tsx b/web/src/pages/Login/GitHubCallback.tsx index 0eab475..2b2922f 100644 --- a/web/src/pages/Login/GitHubCallback.tsx +++ b/web/src/pages/Login/GitHubCallback.tsx @@ -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(); diff --git a/web/src/pages/Login/OIDCCallback.tsx b/web/src/pages/Login/OIDCCallback.tsx index b48225a..6fb4de5 100644 --- a/web/src/pages/Login/OIDCCallback.tsx +++ b/web/src/pages/Login/OIDCCallback.tsx @@ -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(); diff --git a/web/src/pages/Login/index.tsx b/web/src/pages/Login/index.tsx index 3eefd6d..7493f1f 100644 --- a/web/src/pages/Login/index.tsx +++ b/web/src/pages/Login/index.tsx @@ -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); diff --git a/web/src/pages/Monitors/MonitorList.tsx b/web/src/pages/Monitors/MonitorList.tsx index 5737dbc..ead2259 100644 --- a/web/src/pages/Monitors/MonitorList.tsx +++ b/web/src/pages/Monitors/MonitorList.tsx @@ -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']; diff --git a/web/src/pages/Public/MonitorDetail.tsx b/web/src/pages/Public/MonitorDetail.tsx index 7ab0704..4ee2ebe 100644 --- a/web/src/pages/Public/MonitorDetail.tsx +++ b/web/src/pages/Public/MonitorDetail.tsx @@ -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'; diff --git a/web/src/pages/Public/MonitorList.tsx b/web/src/pages/Public/MonitorList.tsx index ad3290c..373f096 100644 --- a/web/src/pages/Public/MonitorList.tsx +++ b/web/src/pages/Public/MonitorList.tsx @@ -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 => { diff --git a/web/src/pages/Public/ServerDetail.tsx b/web/src/pages/Public/ServerDetail.tsx index 4f4d2a5..a90f1d1 100644 --- a/web/src/pages/Public/ServerDetail.tsx +++ b/web/src/pages/Public/ServerDetail.tsx @@ -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'}, diff --git a/web/src/pages/Public/ServerList.tsx b/web/src/pages/Public/ServerList.tsx index 054f68b..8e0a6aa 100644 --- a/web/src/pages/Public/ServerList.tsx +++ b/web/src/pages/Public/ServerList.tsx @@ -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 { diff --git a/web/src/pages/Settings/AlertSettings.tsx b/web/src/pages/Settings/AlertSettings.tsx index ce1976a..a4530ea 100644 --- a/web/src/pages/Settings/AlertSettings.tsx +++ b/web/src/pages/Settings/AlertSettings.tsx @@ -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(); diff --git a/web/src/pages/Settings/MetricsConfig.tsx b/web/src/pages/Settings/MetricsConfig.tsx index 829d706..2408c53 100644 --- a/web/src/pages/Settings/MetricsConfig.tsx +++ b/web/src/pages/Settings/MetricsConfig.tsx @@ -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(); diff --git a/web/src/pages/Settings/NotificationChannels.tsx b/web/src/pages/Settings/NotificationChannels.tsx index bce5fa8..ea376e7 100644 --- a/web/src/pages/Settings/NotificationChannels.tsx +++ b/web/src/pages/Settings/NotificationChannels.tsx @@ -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(); diff --git a/web/src/pages/Settings/SystemConfig.tsx b/web/src/pages/Settings/SystemConfig.tsx index 5921ac2..9cb3f93 100644 --- a/web/src/pages/Settings/SystemConfig.tsx +++ b/web/src/pages/Settings/SystemConfig.tsx @@ -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 = () => { diff --git a/web/src/pages/Settings/index.tsx b/web/src/pages/Settings/index.tsx index 0c8a3b8..4d2d7e0 100644 --- a/web/src/pages/Settings/index.tsx +++ b/web/src/pages/Settings/index.tsx @@ -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 = [ diff --git a/web/src/types/index.ts b/web/src/types/index.ts index 6e74b30..672d2fe 100644 --- a/web/src/types/index.ts +++ b/web/src/types/index.ts @@ -443,45 +443,6 @@ export interface AlertConfig { updatedAt?: number; } -// 通知渠道配置(通过 type 标识,不再使用独立ID) -export interface NotificationChannel { - type: 'dingtalk' | 'wecom' | 'feishu' | 'email' | 'webhook'; // 渠道类型,作为唯一标识 - enabled: boolean; // 是否启用 - config: Record; // 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; // 自定义请求头 - 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; diff --git a/web/tsconfig.app.json b/web/tsconfig.app.json index 16502c4..60968b2 100644 --- a/web/tsconfig.app.json +++ b/web/tsconfig.app.json @@ -15,6 +15,14 @@ "moduleDetection": "force", "noEmit": true, "jsx": "react-jsx", + + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + }, + "allowSyntheticDefaultImports": true }, "include": ["src"] } diff --git a/web/tsconfig.json b/web/tsconfig.json index 1ffef60..696df04 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -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/*" + ] + } + } } diff --git a/web/vite.config.ts b/web/vite.config.ts index 1950513..08a057b 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -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/': {