Подготовить HY2XS к production-сборке
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package util
|
||||
|
||||
func ArrContain[T comparable](arr []T, key T) bool {
|
||||
for _, item := range arr {
|
||||
if item == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func SplitArr[T any](arr []T, num int) [][]T {
|
||||
length := len(arr)
|
||||
if length <= num {
|
||||
return [][]T{arr}
|
||||
}
|
||||
|
||||
quantity := (length + num - 1) / num
|
||||
segments := make([][]T, 0, quantity)
|
||||
|
||||
for i := 0; i < quantity; i++ {
|
||||
end := (i + 1) * num
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
|
||||
segment := arr[i*num : end]
|
||||
segments = append(segments, segment)
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func SHA224String(password string) string {
|
||||
hash := sha256.New224()
|
||||
hash.Write([]byte(password))
|
||||
val := hash.Sum(nil)
|
||||
str := ""
|
||||
for _, v := range val {
|
||||
str += fmt.Sprintf("%02x", v)
|
||||
}
|
||||
return str
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSHA224String(t *testing.T) {
|
||||
println(SHA224String("sysadmin"))
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
"hy2xs-admin/model/constant"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ExportFile t 0/json 1/yaml
|
||||
func ExportFile(filePath string, data any, t int) error {
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
logrus.Errorf("ExportFile create file err filePath: %s err: %v", filePath, err)
|
||||
return errors.New(constant.SysError)
|
||||
}
|
||||
defer file.Close()
|
||||
var bytes []byte
|
||||
if t == 0 {
|
||||
bytes, err = json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
logrus.Errorf("ExportFile Marshal json err filePath: %s err: %v", filePath, err)
|
||||
return errors.New(constant.SysError)
|
||||
}
|
||||
} else if t == 1 {
|
||||
bytes, err = yaml.Marshal(&data)
|
||||
if err != nil {
|
||||
logrus.Errorf("ExportFile Marshal yaml err filePath: %s err: %v", filePath, err)
|
||||
return errors.New(constant.SysError)
|
||||
}
|
||||
}
|
||||
_, err = file.Write(bytes)
|
||||
if err != nil {
|
||||
logrus.Errorf("ExportFile writer WriteString err filePath: %s err: %v", filePath, err)
|
||||
return errors.New(constant.SysError)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func RemoveFile(fileName string) error {
|
||||
if Exists(fileName) {
|
||||
if err := os.Remove(fileName); err != nil {
|
||||
return errors.New("failed to delete file")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadLinesFromBottom Read the file contents sequentially from bottom to top and return the specified number of lines
|
||||
func ReadLinesFromBottom(filePath string, numLines int) ([]string, int, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// Read the file contents line by line and reverse the order of the lines
|
||||
total := 0
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
total++
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Reverse row order
|
||||
for i, j := 0, len(lines)-1; i < j; i, j = i+1, j-1 {
|
||||
lines[i], lines[j] = lines[j], lines[i]
|
||||
}
|
||||
|
||||
// Returns the specified number of rows
|
||||
if len(lines) < numLines {
|
||||
numLines = len(lines)
|
||||
}
|
||||
return lines[:numLines], total, nil
|
||||
}
|
||||
|
||||
func FindFile(dir, filename string) (string, error) {
|
||||
var result string
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && info.Name() == filename {
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result = absPath
|
||||
return errors.New("file found")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil && err.Error() != "file found" {
|
||||
return "", err
|
||||
}
|
||||
if result == "" {
|
||||
return "", fmt.Errorf("file %s not found in directory %s", filename, dir)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/google/go-github/v39/github"
|
||||
)
|
||||
|
||||
var githubClient *github.Client
|
||||
|
||||
func init() {
|
||||
githubClient = github.NewClient(nil)
|
||||
}
|
||||
|
||||
func GetReleaseAssetURL(owner, repo, version, fileName string) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
var release *github.RepositoryRelease
|
||||
var err error
|
||||
if version != "" {
|
||||
release, _, err = githubClient.Repositories.GetReleaseByTag(ctx, owner, repo, version)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get release for version %s: %v", version, err)
|
||||
}
|
||||
} else {
|
||||
releases, _, err := githubClient.Repositories.ListReleases(ctx, owner, repo, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to list releases: %v", err)
|
||||
}
|
||||
if len(releases) == 0 {
|
||||
return "", fmt.Errorf("no releases found")
|
||||
}
|
||||
release = releases[0]
|
||||
}
|
||||
|
||||
assets, _, err := githubClient.Repositories.ListReleaseAssets(ctx, owner, repo, release.GetID(), nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to list release assets: %v", err)
|
||||
}
|
||||
|
||||
for _, asset := range assets {
|
||||
if asset.GetName() == fileName {
|
||||
return asset.GetBrowserDownloadURL(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("file '%s' not found in release '%s'", fileName, release.GetTagName())
|
||||
}
|
||||
|
||||
func ListRelease(owner, repo string) ([]*github.RepositoryRelease, error) {
|
||||
ctx := context.Background()
|
||||
releases, _, err := githubClient.Repositories.ListReleases(ctx, owner, repo, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list releases: %v", err)
|
||||
}
|
||||
return releases, nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hy2xs-admin/model/constant"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func GetHysteria2BinPath() string {
|
||||
return constant.Hysteria2BinPath
|
||||
}
|
||||
|
||||
func GetHysteria2BinName() string {
|
||||
hysteria2FileName := fmt.Sprintf("hysteria-%s-%s", runtime.GOOS, runtime.GOARCH)
|
||||
if runtime.GOOS == "windows" {
|
||||
hysteria2FileName += ".exe"
|
||||
}
|
||||
return hysteria2FileName
|
||||
}
|
||||
|
||||
func DownloadHysteria2(version string) error {
|
||||
hysteria2BinName := GetHysteria2BinName()
|
||||
hysteria2BinPath := GetHysteria2BinPath()
|
||||
|
||||
// Download the latest version of Hysteria2
|
||||
url, err := GetReleaseAssetURL("apernet", "hysteria", version, hysteria2BinName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := http.Get(url)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download file: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("failed to download file, status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
if Exists(hysteria2BinPath) {
|
||||
if err = os.Remove(hysteria2BinPath); err != nil {
|
||||
return fmt.Errorf("failed to remove existing file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(hysteria2BinPath)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file %s: %v", hysteria2BinPath, err)
|
||||
}
|
||||
|
||||
_, err = io.Copy(file, resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to file: %v", err)
|
||||
}
|
||||
|
||||
if err = os.Chmod(hysteria2BinPath, 0755); err != nil {
|
||||
return fmt.Errorf("failed to change file permissions: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Exec(cmd string) (string, error) {
|
||||
command := exec.Command("bash", "-c", cmd)
|
||||
command.Env = os.Environ()
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
logrus.Errorf("execute command failed cmd: %s err: %v", cmd, err)
|
||||
return "", fmt.Errorf("execute command failed cmd: %s", cmd)
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
func Systemctl(action string, unit string) error {
|
||||
_, err := Exec(fmt.Sprintf("systemctl %s %s", action, unit))
|
||||
return err
|
||||
}
|
||||
|
||||
func IsPortAvailable(port uint, network string) bool {
|
||||
if network == "tcp" {
|
||||
listener, err := net.ListenTCP(network, &net.TCPAddr{
|
||||
IP: net.IPv4(0, 0, 0, 0),
|
||||
Port: int(port),
|
||||
})
|
||||
defer func() {
|
||||
if listener != nil {
|
||||
listener.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
logrus.Errorf("port %d is taken err: %s", port, err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if network == "udp" {
|
||||
listener, err := net.ListenUDP("udp", &net.UDPAddr{
|
||||
IP: net.IPv4(0, 0, 0, 0),
|
||||
Port: int(port),
|
||||
})
|
||||
defer func() {
|
||||
if listener != nil {
|
||||
listener.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
logrus.Errorf("port %d is taken err: %s", port, err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func GetCpuPercent() (float64, error) {
|
||||
var err error
|
||||
percent, err := cpu.Percent(time.Second, false)
|
||||
value, err := strconv.ParseFloat(fmt.Sprintf("%.1f", percent[0]), 64)
|
||||
return value, err
|
||||
}
|
||||
|
||||
func GetMemPercent() (float64, error) {
|
||||
var err error
|
||||
memInfo, err := mem.VirtualMemory()
|
||||
value, err := strconv.ParseFloat(fmt.Sprintf("%.1f", memInfo.UsedPercent), 64)
|
||||
return value, err
|
||||
}
|
||||
|
||||
func GetDiskPercent() (float64, error) {
|
||||
var err error
|
||||
parts, err := disk.Partitions(true)
|
||||
diskInfo, err := disk.Usage(parts[0].Mountpoint)
|
||||
value, err := strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.UsedPercent), 64)
|
||||
return value, err
|
||||
}
|
||||
|
||||
func VerifyPort(port string) error {
|
||||
if port != "" {
|
||||
value, err := strconv.ParseInt(port, 10, 64)
|
||||
if err != nil {
|
||||
return errors.New("invalid port value")
|
||||
}
|
||||
if value <= 0 || value > 65535 {
|
||||
return errors.New("the port range is between 0-65535")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package util
|
||||
|
||||
func SplitMap[T any](inputMap map[string]T, chunkSize int) []map[string]T {
|
||||
length := len(inputMap)
|
||||
quantity := (length + chunkSize - 1) / chunkSize
|
||||
segments := make([]map[string]T, 0, quantity)
|
||||
|
||||
var groupIndex int
|
||||
currentGroup := make(map[string]T)
|
||||
|
||||
for key, value := range inputMap {
|
||||
currentGroup[key] = value
|
||||
|
||||
if len(currentGroup) == chunkSize || groupIndex+1 == quantity {
|
||||
// When the current group is full or the last group is reached, add the mapping to the result slice
|
||||
segments = append(segments, currentGroup)
|
||||
currentGroup = make(map[string]T) // Initialize a new mapping
|
||||
groupIndex++
|
||||
}
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package util
|
||||
|
||||
import "crypto/rand"
|
||||
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
func RandomString(length int) (string, error) {
|
||||
bytes := make([]byte, length)
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i := range bytes {
|
||||
bytes[i] = charset[int(bytes[i])%len(charset)]
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package util
|
||||
|
||||
import "strings"
|
||||
|
||||
func CompareVersion(version1, version2 string) int {
|
||||
v1 := strings.Split(version1, ".")
|
||||
v2 := strings.Split(version2, ".")
|
||||
|
||||
// Compare major version numbers
|
||||
if v1[0] > v2[0] {
|
||||
return 1
|
||||
} else if v1[0] < v2[0] {
|
||||
return -1
|
||||
}
|
||||
|
||||
// If the major version numbers are the same, compare the minor version numbers
|
||||
if len(v1) > 1 && len(v2) > 1 {
|
||||
if v1[1] > v2[1] {
|
||||
return 1
|
||||
} else if v1[1] < v2[1] {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// If the major and minor versions are the same, compare the revision numbers
|
||||
if len(v1) > 2 && len(v2) > 2 {
|
||||
if v1[2] > v2[2] {
|
||||
return 1
|
||||
} else if v1[2] < v2[2] {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// The version number is exactly the same
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user