refactor: packages to pkg

This commit is contained in:
耗子
2023-07-20 01:06:49 +08:00
parent ec7138ae2a
commit 5ce65653ad
33 changed files with 260 additions and 260 deletions
+23
View File
@@ -0,0 +1,23 @@
package tools
import (
"os"
"runtime"
)
// IsDebian 判断是否是 Debian 系统
func IsDebian() bool {
_, err := os.Stat("/etc/debian_version")
return err == nil
}
// IsRHEL 判断是否是 RHEL 系统
func IsRHEL() bool {
_, err := os.Stat("/etc/redhat-release")
return err == nil
}
// IsArm 判断是否是 ARM 架构
func IsArm() bool {
return runtime.GOARCH == "arm"
}
+27
View File
@@ -0,0 +1,27 @@
package tools
import (
"testing"
"github.com/stretchr/testify/suite"
)
type OSHelperTestSuite struct {
suite.Suite
}
func TestOSHelperTestSuite(t *testing.T) {
suite.Run(t, &OSHelperTestSuite{})
}
func (s *OSHelperTestSuite) TestIsDebian() {
s.True(IsDebian())
}
func (s *OSHelperTestSuite) TestIsRHEL() {
s.False(IsRHEL())
}
func (s *OSHelperTestSuite) TestIsArm() {
s.False(IsArm())
}
+106
View File
@@ -0,0 +1,106 @@
package tools
import (
"crypto/md5"
"crypto/rand"
"fmt"
"io"
"reflect"
"strings"
"unicode/utf8"
)
// Empty 类似于 PHP 的 empty() 函数
func Empty(val interface{}) bool {
if val == nil {
return true
}
v := reflect.ValueOf(val)
switch v.Kind() {
case reflect.String, reflect.Array:
return v.Len() == 0
case reflect.Map, reflect.Slice:
return v.Len() == 0 || v.IsNil()
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
}
return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface())
}
// FirstElement 安全地获取 args[0],避免 panic: runtime error: index out of range
func FirstElement(args []string) string {
if len(args) > 0 {
return args[0]
}
return ""
}
// RandomNumber 生成长度为 length 随机数字字符串
func RandomNumber(length int) string {
table := [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}
b := make([]byte, length)
n, err := io.ReadAtLeast(rand.Reader, b, length)
if n != length {
panic(err)
}
for i := 0; i < len(b); i++ {
b[i] = table[int(b[i])%len(table)]
}
return string(b)
}
// RandomString 生成长度为 length 的随机字符串
func RandomString(length int) string {
b := make([]byte, length)
_, err := rand.Read(b)
if err != nil {
panic(err)
}
letters := "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i, v := range b {
b[i] = letters[v%byte(len(letters))]
}
return string(b)
}
// MD5 生成字符串的 MD5 值
func MD5(str string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}
// FormatBytes 格式化bytes
func FormatBytes(size float64) string {
units := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
i := 0
for ; size >= 1024 && i < len(units); i++ {
size /= 1024
}
return fmt.Sprintf("%.2f %s", size, units[i])
}
// Cut 裁剪字符串
func Cut(str, begin, end string) string {
bIndex := strings.Index(str, begin)
eIndex := strings.Index(str, end)
if bIndex == -1 || eIndex == -1 || bIndex > eIndex {
return ""
}
b := utf8.RuneCountInString(str[:bIndex]) + utf8.RuneCountInString(begin)
e := utf8.RuneCountInString(str[:eIndex])
if b > e {
return ""
}
return string([]rune(str)[b:e])
}
+64
View File
@@ -0,0 +1,64 @@
package tools
import (
"testing"
"github.com/stretchr/testify/suite"
)
type StringHelperTestSuite struct {
suite.Suite
}
func TestStringHelperTestSuite(t *testing.T) {
suite.Run(t, &StringHelperTestSuite{})
}
func (s *StringHelperTestSuite) TestEmpty() {
s.True(Empty(""))
s.True(Empty(nil))
s.True(Empty([]string{}))
s.True(Empty(map[string]string{}))
s.True(Empty(0))
s.True(Empty(0.0))
s.True(Empty(false))
s.False(Empty(" "))
s.False(Empty([]string{"Panel"}))
s.False(Empty(map[string]string{"Panel": "HaoZi"}))
s.False(Empty(1))
s.False(Empty(1.0))
s.False(Empty(true))
}
func (s *StringHelperTestSuite) TestFirstElement() {
s.Equal("HaoZi", FirstElement([]string{"HaoZi"}))
}
func (s *StringHelperTestSuite) TestRandomNumber() {
s.Len(RandomNumber(10), 10)
}
func (s *StringHelperTestSuite) TestRandomString() {
s.Len(RandomString(10), 10)
}
func (s *StringHelperTestSuite) TestMD5() {
s.Equal("e10adc3949ba59abbe56e057f20f883e", MD5("123456"))
}
func (s *StringHelperTestSuite) TestFormatBytes() {
s.Equal("1.00 B", FormatBytes(1))
s.Equal("1.00 KB", FormatBytes(1024))
s.Equal("1.00 MB", FormatBytes(1024*1024))
s.Equal("1.00 GB", FormatBytes(1024*1024*1024))
s.Equal("1.00 TB", FormatBytes(1024*1024*1024*1024))
s.Equal("1.00 PB", FormatBytes(1024*1024*1024*1024*1024))
s.Equal("1.00 EB", FormatBytes(1024*1024*1024*1024*1024*1024))
s.Equal("1.00 ZB", FormatBytes(1024*1024*1024*1024*1024*1024*1024))
s.Equal("1.00 YB", FormatBytes(1024*1024*1024*1024*1024*1024*1024*1024))
}
func (s *StringHelperTestSuite) TestCut() {
s.Equal("aoZ", Cut("HaoZi", "H", "i"))
}
+115
View File
@@ -0,0 +1,115 @@
package tools
import (
"os"
"os/exec"
"path/filepath"
"github.com/goravel/framework/facades"
)
// WriteFile 写入文件
func WriteFile(path string, data string, permission os.FileMode) bool {
if err := os.MkdirAll(filepath.Dir(path), permission); err != nil {
facades.Log().Errorf("[面板][Helpers] 创建目录失败: %s", err.Error())
return false
}
err := os.WriteFile(path, []byte(data), permission)
if err != nil {
facades.Log().Errorf("[面板][Helpers] 写入文件 %s 失败: %s", path, err.Error())
return false
}
return true
}
// ReadFile 读取文件
func ReadFile(path string) string {
data, err := os.ReadFile(path)
if err != nil {
facades.Log().Errorf("[面板][Helpers] 读取文件 %s 失败: %s", path, err.Error())
return ""
}
return string(data)
}
// RemoveFile 删除文件
func RemoveFile(path string) bool {
if err := os.Remove(path); err != nil {
facades.Log().Errorf("[面板][Helpers] 删除文件 %s 失败: %s", path, err.Error())
return false
}
return true
}
// ExecShell 执行 shell 命令
func ExecShell(shell string) string {
cmd := exec.Command("bash", "-c", shell)
output, err := cmd.CombinedOutput()
if err != nil {
facades.Log().Errorf("[面板][Helpers] 执行命令 $s 失败: %s", shell, err.Error())
return ""
}
return string(output)
}
// ExecShellAsync 异步执行 shell 命令
func ExecShellAsync(shell string) {
cmd := exec.Command("bash", "-c", shell)
err := cmd.Start()
if err != nil {
facades.Log().Errorf("[面板][Helpers] 执行命令 $s 失败: %s", shell, err.Error())
}
go func() {
err := cmd.Wait()
if err != nil {
facades.Log().Errorf("[面板][Helpers] 执行命令 $s 失败: %s", shell, err.Error())
}
}()
}
// Mkdir 创建目录
func Mkdir(path string, permission os.FileMode) bool {
if err := os.MkdirAll(path, permission); err != nil {
facades.Log().Errorf("[面板][Helpers] 创建目录 %s 失败: %s", path, err.Error())
return false
}
return true
}
// Chmod 修改文件权限
func Chmod(path string, permission os.FileMode) bool {
if err := os.Chmod(path, permission); err != nil {
facades.Log().Errorf("[面板][Helpers] 修改文件 %s 权限失败: %s", path, err.Error())
return false
}
return true
}
// Chown 修改路径所有者
func Chown(path, user, group string) bool {
cmd := exec.Command("chown", "-R", user+":"+group, path)
err := cmd.Run()
if err != nil {
facades.Log().Errorf("[面板][Helpers] 修改路径 %s 所有者失败: %s", path, err.Error())
return false
}
return true
}
// Exists 判断路径是否存在
func Exists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
+101
View File
@@ -0,0 +1,101 @@
package tools
import (
"os"
"os/user"
"testing"
"time"
"github.com/stretchr/testify/suite"
)
type SystemHelperTestSuite struct {
suite.Suite
}
func TestSystemHelperTestSuite(t *testing.T) {
suite.Run(t, &SystemHelperTestSuite{})
}
func (s *SystemHelperTestSuite) TestWriteFile() {
filePath := "/tmp/testfile"
defer os.Remove(filePath)
s.True(WriteFile(filePath, "test data", 0644))
s.FileExists(filePath)
content, _ := os.ReadFile(filePath)
s.Equal("test data", string(content))
}
func (s *SystemHelperTestSuite) TestReadFile() {
filePath := "/tmp/testfile"
defer os.Remove(filePath)
err := os.WriteFile(filePath, []byte("test data"), 0644)
s.Nil(err)
s.Equal("test data", ReadFile(filePath))
}
func (s *SystemHelperTestSuite) TestRemoveFile() {
filePath := "/tmp/testfile"
err := os.WriteFile(filePath, []byte("test data"), 0644)
s.Nil(err)
s.True(RemoveFile(filePath))
}
func (s *SystemHelperTestSuite) TestExecShell() {
s.Equal("test\n", ExecShell("echo 'test'"))
}
func (s *SystemHelperTestSuite) TestExecShellAsync() {
command := "echo 'test' > /tmp/testfile"
defer os.Remove("/tmp/testfile")
ExecShellAsync(command)
time.Sleep(time.Second)
content, _ := os.ReadFile("/tmp/testfile")
s.Equal("test\n", string(content))
}
func (s *SystemHelperTestSuite) TestMkdir() {
dirPath := "/tmp/testdir"
defer os.RemoveAll(dirPath)
s.True(Mkdir(dirPath, 0755))
}
func (s *SystemHelperTestSuite) TestChmod() {
filePath := "/tmp/testfile"
defer os.Remove(filePath)
err := os.WriteFile(filePath, []byte("test data"), 0644)
s.Nil(err)
s.True(Chmod(filePath, 0755))
}
func (s *SystemHelperTestSuite) TestChown() {
filePath := "/tmp/testfile"
defer os.Remove(filePath)
err := os.WriteFile(filePath, []byte("test data"), 0644)
s.Nil(err)
currentUser, err := user.Current()
s.Nil(err)
groups, err := currentUser.GroupIds()
s.Nil(err)
s.True(Chown(filePath, currentUser.Username, groups[0]))
}
func (s *SystemHelperTestSuite) TestExists() {
s.True(Exists("/tmp"))
s.False(Exists("/tmp/123"))
}
+130
View File
@@ -0,0 +1,130 @@
// Package tools 存放辅助方法
package tools
import (
"errors"
"os"
"os/exec"
"strings"
"time"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/host"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
"github.com/shirou/gopsutil/net"
)
// MonitoringInfo 监控信息
type MonitoringInfo struct {
Cpus []cpu.InfoStat `json:"cpus"`
Percent []float64 `json:"percent"`
Load *load.AvgStat `json:"load"`
Host *host.InfoStat `json:"host"`
Mem *mem.VirtualMemoryStat `json:"mem"`
Swap *mem.SwapMemoryStat `json:"swap"`
Net []net.IOCountersStat `json:"net"`
DiskIO map[string]disk.IOCountersStat `json:"disk_io"`
Disk []disk.PartitionStat `json:"disk"`
DiskUsage map[string]*disk.UsageStat `json:"disk_usage"`
}
// GetMonitoringInfo 获取监控数据
func GetMonitoringInfo() MonitoringInfo {
var res MonitoringInfo
res.Cpus, _ = cpu.Info()
res.Percent, _ = cpu.Percent(time.Second, false)
res.Load, _ = load.Avg()
res.Host, _ = host.Info()
res.Mem, _ = mem.VirtualMemory()
res.Swap, _ = mem.SwapMemory()
res.Net, _ = net.IOCounters(true)
res.DiskIO, _ = disk.IOCounters()
res.Disk, _ = disk.Partitions(true)
res.DiskUsage = make(map[string]*disk.UsageStat)
for _, partition := range res.Disk {
if strings.HasPrefix(partition.Mountpoint, "/dev") || strings.HasPrefix(partition.Mountpoint, "/sys") || strings.HasPrefix(partition.Mountpoint, "/proc") || strings.HasPrefix(partition.Mountpoint, "/run") || strings.HasPrefix(partition.Mountpoint, "/boot") || strings.HasPrefix(partition.Mountpoint, "/usr") {
continue
}
usage, _ := disk.Usage(partition.Mountpoint)
res.DiskUsage[partition.Mountpoint] = usage
}
return res
}
type PanelInfo struct {
Name string `json:"name"`
Version string `json:"version"`
DownloadUrl string `json:"download_url"`
Body string `json:"body"`
Date string `json:"date"`
}
// GetLatestPanelVersion 获取最新版本
func GetLatestPanelVersion() (PanelInfo, error) {
var info PanelInfo
cmd := exec.Command("/bin/bash", "-c", "curl \"https://api.github.com/repos/HaoZi-Team/Panel/releases/latest\"")
output, err := cmd.Output()
if err != nil {
return info, errors.New("获取最新版本失败")
}
file, err := os.CreateTemp("", "panel")
if err != nil {
return info, errors.New("创建临时文件失败")
}
defer os.Remove(file.Name())
_, err = file.Write(output)
if err != nil {
return info, errors.New("写入临时文件失败")
}
err = file.Close()
if err != nil {
return info, errors.New("关闭临时文件失败")
}
fileName := file.Name()
name := exec.Command("/bin/bash", "-c", "jq -r '.name' "+fileName)
version := exec.Command("/bin/bash", "-c", "jq -r '.tag_name' "+fileName)
body := exec.Command("/bin/bash", "-c", "jq -r '.body' "+fileName)
date := exec.Command("/bin/bash", "-c", "jq -r '.published_at' "+fileName)
nameOutput, _ := name.Output()
versionOutput, _ := version.Output()
bodyOutput, _ := body.Output()
dateOutput, _ := date.Output()
info.Name = strings.TrimSpace(string(nameOutput))
info.Version = strings.TrimSpace(string(versionOutput))
info.Body = strings.TrimSpace(string(bodyOutput))
info.Date = strings.TrimSpace(string(dateOutput))
if IsArm() {
downloadUrl := exec.Command("/bin/bash", "-c", "jq -r '.assets[] | select(.name | contains(\"arm64\")) | .browser_download_url' "+fileName)
downloadUrlOutput, _ := downloadUrl.Output()
info.DownloadUrl = strings.TrimSpace(string(downloadUrlOutput))
} else {
downloadUrl := exec.Command("/bin/bash", "-c", "jq -r '.assets[] | select(.name | contains(\"amd64v3\")) | .browser_download_url' "+fileName)
downloadUrlOutput, _ := downloadUrl.Output()
info.DownloadUrl = strings.TrimSpace(string(downloadUrlOutput))
}
return info, nil
}
// UpdatePanel 更新面板
func UpdatePanel() error {
panelInfo, err := GetLatestPanelVersion()
if err != nil {
return err
}
cmd := exec.Command("/bin/bash", "-c", "wget -O panel.tar.gz "+panelInfo.DownloadUrl+" && tar -zxvf panel.tar.gz && rm -rf panel.tar.gz && chmod +x panel && ./panel artisan migrate")
_, err = cmd.Output()
if err != nil {
return errors.New("更新面板失败")
}
return nil
}
+19
View File
@@ -0,0 +1,19 @@
package tools
import (
"testing"
"github.com/stretchr/testify/suite"
)
type HelperTestSuite struct {
suite.Suite
}
func TestHelperTestSuite(t *testing.T) {
suite.Run(t, &HelperTestSuite{})
}
func (s *HelperTestSuite) TestGetMonitoringInfo() {
s.NotNil(GetMonitoringInfo())
}