diff --git a/.vscode/settings.json b/.vscode/settings.json index a161def..4da5073 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,16 +19,12 @@ "markdown" ], "cSpell.words": [ - "antfu", "axios", "bumpp", - "chatgpt", - "chenzhaoyu", "commitlint", "davinci", "dockerhub", "esno", - "GPTAPI", "highlightjs", "hljs", "iconify", @@ -39,7 +35,6 @@ "mdhljs", "mila", "nodata", - "OPENAI", "pinia", "Popconfirm", "rushstack", @@ -50,8 +45,7 @@ "Typecheck", "unplugin", "VITE", - "vueuse", - "Zhao" + "vueuse" ], "i18n-ally.enabledParsers": [ "ts" diff --git a/index.html b/index.html index b8f7493..24e2ca0 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ - Sun Panel + Sun-Panel diff --git a/service/api/api_v1/common/apiData/systemApiStructs/monitor.go b/service/api/api_v1/common/apiData/systemApiStructs/monitor.go new file mode 100644 index 0000000..f39f325 --- /dev/null +++ b/service/api/api_v1/common/apiData/systemApiStructs/monitor.go @@ -0,0 +1,5 @@ +package systemApiStructs + +type MonitorGetDiskStateByPathReq struct { + Path string `json:"path"` +} diff --git a/service/api/api_v1/system/A_ENTER.go b/service/api/api_v1/system/A_ENTER.go index f1b8636..490e69e 100644 --- a/service/api/api_v1/system/A_ENTER.go +++ b/service/api/api_v1/system/A_ENTER.go @@ -9,4 +9,5 @@ type ApiSystem struct { RegisterApi RegisterApi NoticeApi NoticeApi ModuleConfigApi ModuleConfigApi + MonitorApi MonitorApi } diff --git a/service/api/api_v1/system/monitor.go b/service/api/api_v1/system/monitor.go new file mode 100644 index 0000000..7a14d2c --- /dev/null +++ b/service/api/api_v1/system/monitor.go @@ -0,0 +1,96 @@ +package system + +import ( + "sun-panel/api/api_v1/common/apiData/systemApiStructs" + "sun-panel/api/api_v1/common/apiReturn" + "sun-panel/global" + "sun-panel/lib/monitor" + "time" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" +) + +type MonitorApi struct{} + +const cacheSecond = 3 + +// 弃用 +func (a *MonitorApi) GetAll(c *gin.Context) { + if value, ok := global.SystemMonitor.Get("value"); ok { + apiReturn.SuccessData(c, value) + return + } + apiReturn.Error(c, "failed") +} + +func (a *MonitorApi) GetCpuState(c *gin.Context) { + if v, ok := global.SystemMonitor.Get(global.SystemMonitor_CPU_INFO); ok { + global.Logger.Debugln("读取缓存的的CPU信息") + apiReturn.SuccessData(c, v) + return + } + cpuInfo, err := monitor.GetCPUInfo() + + if err != nil { + apiReturn.Error(c, "failed") + return + } + // 缓存 + global.SystemMonitor.Set(global.SystemMonitor_CPU_INFO, cpuInfo, cacheSecond*time.Second) + apiReturn.SuccessData(c, cpuInfo) +} + +func (a *MonitorApi) GetMemonyState(c *gin.Context) { + if v, ok := global.SystemMonitor.Get(global.SystemMonitor_MEMORY_INFO); ok { + global.Logger.Debugln("读取缓存的的RAM信息") + apiReturn.SuccessData(c, v) + return + } + memoryInfo, err := monitor.GetMemoryInfo() + + if err != nil { + apiReturn.Error(c, "failed") + return + } + + // 缓存 + global.SystemMonitor.Set(global.SystemMonitor_MEMORY_INFO, memoryInfo, cacheSecond*time.Second) + apiReturn.SuccessData(c, memoryInfo) +} + +func (a *MonitorApi) GetDiskStateByPath(c *gin.Context) { + + req := systemApiStructs.MonitorGetDiskStateByPathReq{} + if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil { + apiReturn.ErrorParamFomat(c, err.Error()) + return + } + + cacheDiskName := global.SystemMonitor_DISK_INFO + req.Path + + if v, ok := global.SystemMonitor.Get(cacheDiskName); ok { + global.Logger.Debugln("读取缓存的的DISK信息") + apiReturn.SuccessData(c, v) + return + } + + diskState, err := monitor.GetDiskInfoByPath(req.Path) + if err != nil { + apiReturn.Error(c, "failed") + return + } + + // 缓存 + global.SystemMonitor.Set(cacheDiskName, diskState, cacheSecond*time.Second) + apiReturn.SuccessData(c, diskState) +} + +func (a *MonitorApi) GetDiskMountpoints(c *gin.Context) { + if list, err := monitor.GetDiskMountpoints(); err != nil { + apiReturn.Error(c, err.Error()) + return + } else { + apiReturn.SuccessData(c, list) + } +} diff --git a/service/assets/version b/service/assets/version index 58ba41a..0a8912e 100644 --- a/service/assets/version +++ b/service/assets/version @@ -1 +1 @@ -8|1.2.1 \ No newline at end of file +9|1.3.0-beta24-01-09 \ No newline at end of file diff --git a/service/global/global.go b/service/global/global.go index 0e133c6..b24a5c3 100644 --- a/service/global/global.go +++ b/service/global/global.go @@ -35,5 +35,6 @@ var ( Db *gorm.DB RedisDb *redis.Client SystemSetting *systemSetting.SystemSettingCache + SystemMonitor cache.Cacher[interface{}] RateLimit *RateLimiter ) diff --git a/service/global/monitor.go b/service/global/monitor.go new file mode 100644 index 0000000..e3e3db7 --- /dev/null +++ b/service/global/monitor.go @@ -0,0 +1,18 @@ +package global + +import ( + "sun-panel/lib/monitor" +) + +const ( + SystemMonitor_CPU_INFO = "CPU_INFO" + SystemMonitor_MEMORY_INFO = "MEMORY_INFO" + SystemMonitor_DISK_INFO = "DISK_INFO" +) + +type ModelSystemMonitor struct { + CPUInfo monitor.CPUInfo `json:"cpuInfo"` + DiskInfo []monitor.DiskInfo `json:"diskInfo"` + NetIOCountersInfo []monitor.NetIOCountersInfo `json:"netIOCountersInfo"` + MemoryInfo monitor.MemoryInfo `json:"memoryInfo"` +} diff --git a/service/initialize/A_ENTER.go b/service/initialize/A_ENTER.go index 449561f..78abfd7 100644 --- a/service/initialize/A_ENTER.go +++ b/service/initialize/A_ENTER.go @@ -17,6 +17,7 @@ import ( "sun-panel/lib/cmn" "sun-panel/models" "sun-panel/structs" + "time" "log" @@ -88,6 +89,7 @@ func InitApp() error { // 其他的初始化 global.VerifyCodeCachePool = other.InitVerifyCodeCachePool() global.SystemSetting = systemSettingCache.InItSystemSettingCache() + global.SystemMonitor = global.NewCache[interface{}](5*time.Hour, -1, "systemMonitorCache") return nil } diff --git a/service/initialize/systemMonitor/systemMonitor.go b/service/initialize/systemMonitor/systemMonitor.go new file mode 100644 index 0000000..1b155f1 --- /dev/null +++ b/service/initialize/systemMonitor/systemMonitor.go @@ -0,0 +1,53 @@ +package systemMonitor + +import ( + "sun-panel/global" + "sun-panel/lib/cache" + "sun-panel/lib/monitor" + "time" +) + +func Start(cacher cache.Cacher[global.ModelSystemMonitor], interval time.Duration) { + go func() { + + ticker := time.NewTicker(interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + go func() { + monitorInfo := GetInfo() + // jsonByte, _ := json.Marshal(monitorInfo) + // fmt.Println("系统监控:", string(jsonByte)) + cacher.SetDefault("value", monitorInfo) + }() + } + } + + }() + +} + +func GetInfo() global.ModelSystemMonitor { + + var modelSystemMonitor global.ModelSystemMonitor + + if cpuInfo, err := monitor.GetCPUInfo(); err == nil { + modelSystemMonitor.CPUInfo = cpuInfo + } + + if v, err := monitor.GetDiskInfo(); err == nil { + modelSystemMonitor.DiskInfo = v + } + + if v, err := monitor.GetNetIOCountersInfo(); err == nil { + modelSystemMonitor.NetIOCountersInfo = v + } + + if v, err := monitor.GetMemoryInfo(); err == nil { + modelSystemMonitor.MemoryInfo = v + } + + return modelSystemMonitor +} diff --git a/service/lib/cache/base.go b/service/lib/cache/base.go index b522d70..99dbf1e 100644 --- a/service/lib/cache/base.go +++ b/service/lib/cache/base.go @@ -4,6 +4,11 @@ import ( "time" ) +const ( + CACHE_DRIVE_REDIS = "redis" + CACHE_DRIVE_MEMORY = "memory" +) + // 缓存接口-支持Redis和内存使用 type Cacher[T any] interface { // 设置 diff --git a/service/lib/monitor/monitor.go b/service/lib/monitor/monitor.go new file mode 100644 index 0000000..72c2904 --- /dev/null +++ b/service/lib/monitor/monitor.go @@ -0,0 +1,149 @@ +package monitor + +import ( + "time" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/mem" + "github.com/shirou/gopsutil/v3/net" +) + +type CPUInfo struct { + CoreCount int32 `json:"coreCount"` + CPUNum int `json:"cpuNum"` + Model string `json:"model"` + Usages []float64 `json:"usages"` +} + +type DiskInfo struct { + Mountpoint string `json:"mountpoint"` + Total uint64 `json:"total"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + UsedPercent float64 `json:"usedPercent"` +} + +type NetIOCountersInfo struct { + BytesSent uint64 `json:"bytesSent"` + BytesRecv uint64 `json:"bytesRecv"` + Name string `json:"name"` +} + +type MemoryInfo struct { + Total uint64 `json:"total"` + Free uint64 `json:"free"` + Used uint64 `json:"used"` + UsedPercent float64 `json:"usedPercent"` +} + +// 获取CPU信息 +func GetCPUInfo() (CPUInfo, error) { + cpuInfoRes := CPUInfo{} + cpuInfo, err := cpu.Info() + + if err == nil && len(cpuInfo) > 0 { + cpuInfoRes.CoreCount = cpuInfo[0].Cores + cpuInfoRes.Model = cpuInfo[0].ModelName + } + numCPU, _ := cpu.Counts(true) + cpuInfoRes.CPUNum = numCPU + cpuPercentages, err := cpu.Percent(time.Second, true) + cpuInfoRes.Usages = cpuPercentages + + return cpuInfoRes, err +} + +// 获取内存信息 单位:MB +func GetMemoryInfo() (MemoryInfo, error) { + memoryInfo := MemoryInfo{} + // 获取内存信息 + memInfo, err := mem.VirtualMemory() + if err == nil { + memoryInfo.Free = memInfo.Free + memoryInfo.Total = memInfo.Total + memoryInfo.Used = memInfo.Used + memoryInfo.UsedPercent = memInfo.UsedPercent + } + + return memoryInfo, err +} + +// 获取每个磁盘分区使用情况 +func GetDiskInfo() ([]DiskInfo, error) { + disks := []DiskInfo{} + // 获取所有磁盘分区的信息 + partitions, err := disk.Partitions(true) + if err != nil { + return disks, err + } + + for _, partition := range partitions { + usage, err := disk.Usage(partition.Mountpoint) + if err != nil { + // fmt.Printf("Error getting disk usage for %s: %v\n", partition.Mountpoint, err) + continue + } + + disks = append(disks, DiskInfo{ + Mountpoint: partition.Mountpoint, + Total: usage.Total / 1024 / 1024, + Used: usage.Used / 1024 / 1024, + Free: usage.Free / 1024 / 1024, + UsedPercent: usage.UsedPercent, + }) + } + + return disks, nil +} + +func GetDiskMountpoints() ([]disk.PartitionStat, error) { + return disk.Partitions(true) +} + +func GetDiskInfoByPath(path string) (*DiskInfo, error) { + diskInfo := DiskInfo{} + usage, err := disk.Usage(path) + if err != nil { + return nil, err + } + diskInfo.Free = usage.Free + diskInfo.Mountpoint = usage.Path + diskInfo.Total = usage.Total + diskInfo.Used = usage.Used + diskInfo.UsedPercent = usage.UsedPercent + return &diskInfo, nil +} + +// 获取网络统计信息 +func GetNetIOCountersInfo() ([]NetIOCountersInfo, error) { + netInfo := []NetIOCountersInfo{} + netStats, err := net.IOCounters(true) + if err == nil { + for _, netStat := range netStats { + netInfo = append(netInfo, NetIOCountersInfo{ + BytesRecv: netStat.BytesRecv, + BytesSent: netStat.BytesSent, + Name: netStat.Name, + }) + + } + } + return netInfo, err +} + +// func GetCountDiskInfo() { +// // 获取所有磁盘的总使用情况 +// allUsage, err := disk.Usage("/") +// if err != nil { +// fmt.Printf("Error getting total disk usage: %v\n", err) +// return +// } + +// // 打印所有磁盘的总使用情况 +// fmt.Println("Total Disk Usage:") +// fmt.Printf("Total: %d MB\n", allUsage.Total/1024/1024) +// fmt.Printf("Used: %d MB\n", allUsage.Used/1024/1024) +// fmt.Printf("Free: %d MB\n", allUsage.Free/1024/1024) +// fmt.Printf("Usage: %.2f%%\n", allUsage.UsedPercent) +// } diff --git a/service/router/system/A_ENTER.go b/service/router/system/A_ENTER.go index 62e8b7c..c65dedf 100644 --- a/service/router/system/A_ENTER.go +++ b/service/router/system/A_ENTER.go @@ -11,4 +11,5 @@ func Init(routerGroup *gin.RouterGroup) { InitRegister(routerGroup) InitNoticeRouter(routerGroup) InitModuleConfigRouter(routerGroup) + InitMonitorRouter(routerGroup) } diff --git a/service/router/system/monitor.go b/service/router/system/monitor.go new file mode 100644 index 0000000..d4f3ff0 --- /dev/null +++ b/service/router/system/monitor.go @@ -0,0 +1,23 @@ +package system + +import ( + "sun-panel/api/api_v1" + "sun-panel/api/api_v1/middleware" + + "github.com/gin-gonic/gin" +) + +func InitMonitorRouter(router *gin.RouterGroup) { + api := api_v1.ApiGroupApp.ApiSystem.MonitorApi + r := router.Group("", middleware.LoginInterceptor) + r.POST("/system/monitor/getDiskMountpoints", api.GetDiskMountpoints) + + // 公开模式 + rPublic := router.Group("", middleware.PublicModeInterceptor) + { + rPublic.POST("/system/monitor/getAll", api.GetAll) + rPublic.POST("/system/monitor/getCpuState", api.GetCpuState) + rPublic.POST("/system/monitor/getDiskStateByPath", api.GetDiskStateByPath) + rPublic.POST("/system/monitor/getMemonyState", api.GetMemonyState) + } +} diff --git a/src/api/system/systemMonitor.ts b/src/api/system/systemMonitor.ts new file mode 100644 index 0000000..0e17ff2 --- /dev/null +++ b/src/api/system/systemMonitor.ts @@ -0,0 +1,32 @@ +import { post } from '@/utils/request' + +export function getAll() { + return post({ + url: '/system/monitor/getAll', + }) +} + +export function getCpuState() { + return post({ + url: '/system/monitor/getCpuState', + }) +} + +export function getDiskStateByPath(path: string) { + return post({ + url: '/system/monitor/getDiskStateByPath', + data: { path }, + }) +} + +export function getMemonyState() { + return post({ + url: '/system/monitor/getMemonyState', + }) +} + +export function getDiskMountpoints() { + return post({ + url: '/system/monitor/getDiskMountpoints', + }) +} diff --git a/src/assets/svg-icons/clarity-hard-disk-solid.svg b/src/assets/svg-icons/clarity-hard-disk-solid.svg new file mode 100644 index 0000000..f97333e --- /dev/null +++ b/src/assets/svg-icons/clarity-hard-disk-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg-icons/ion-language.svg b/src/assets/svg-icons/ion-language.svg new file mode 100644 index 0000000..0a25377 --- /dev/null +++ b/src/assets/svg-icons/ion-language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg-icons/material-symbols-memory-alt-rounded.svg b/src/assets/svg-icons/material-symbols-memory-alt-rounded.svg new file mode 100644 index 0000000..0f5dcfa --- /dev/null +++ b/src/assets/svg-icons/material-symbols-memory-alt-rounded.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg-icons/solar-cpu-bold.svg b/src/assets/svg-icons/solar-cpu-bold.svg new file mode 100644 index 0000000..590f39d --- /dev/null +++ b/src/assets/svg-icons/solar-cpu-bold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/apps/About/index.vue b/src/components/apps/About/index.vue index 4de4cab..381dce9 100644 --- a/src/components/apps/About/index.vue +++ b/src/components/apps/About/index.vue @@ -35,7 +35,7 @@ onMounted(() => { @@ -43,19 +43,23 @@ onMounted(() => {
- QQ交流群:276594668 + {{ $t('apps.about.issue') }}Github Issues +
+ +
+ {{ $t('apps.about.QQGroup') }}{{ $t("apps.about.addQQGroupUrl") }} | - 二维码(推荐) + {{ $t('apps.about.QR') }}
- 开发者:红烧猎人 | 🧧打赏 + {{ $t('apps.about.author') }}红烧猎人 | {{ $t('apps.about.donate') }}
diff --git a/src/components/apps/ImportExport/index.vue b/src/components/apps/ImportExport/index.vue index 47a17d5..59d9caf 100644 --- a/src/components/apps/ImportExport/index.vue +++ b/src/components/apps/ImportExport/index.vue @@ -91,9 +91,9 @@ async function importIcons(): Promise { } catch (error) { if (error instanceof Error) - return `发生错误: ${error.message}` + return `${t('common.failed')}: ${error.message}` else - return '发生未知错误' + return t('common.unknownError') } } @@ -162,7 +162,7 @@ function handleFileChange(options: { file: UploadFileInfo; fileList: Array