This commit is contained in:
chermack
2026-07-26 00:09:57 +08:00
commit 059be96536
134 changed files with 19678 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
package util
import (
"strconv"
"strings"
)
const (
// BYTE 字节
BYTE = 1 << (10 * iota)
// KILOBYTE 千字节
KILOBYTE
// MEGABYTE 兆字节
MEGABYTE
// GIGABYTE 吉字节
GIGABYTE
// TERABYTE 太字节
TERABYTE
// PETABYTE 拍字节
PETABYTE
// EXABYTE 艾字节
EXABYTE
)
// Bytefmt returns a human-readable byte string of the form 10M, 12.5K, and so forth. The following units are available:
// E: Exabyte
// P: Petabyte
// T: Terabyte
// G: Gigabyte
// M: Megabyte
// K: Kilobyte
// B: Byte
// The unit that results in the smallest number greater than or equal to 1 is always chosen.
func Bytefmt(bytes uint64) string {
unit := ""
value := float64(bytes)
switch {
case bytes >= EXABYTE:
unit = "E"
value = value / EXABYTE
case bytes >= PETABYTE:
unit = "P"
value = value / PETABYTE
case bytes >= TERABYTE:
unit = "T"
value = value / TERABYTE
case bytes >= GIGABYTE:
unit = "G"
value = value / GIGABYTE
case bytes >= MEGABYTE:
unit = "M"
value = value / MEGABYTE
case bytes >= KILOBYTE:
unit = "K"
value = value / KILOBYTE
case bytes >= BYTE:
unit = "B"
case bytes == 0:
return "0B"
}
result := strconv.FormatFloat(value, 'f', 2, 64)
result = strings.TrimSuffix(result, ".0")
return result + unit
}
+155
View File
@@ -0,0 +1,155 @@
package util
import (
"bufio"
"fmt"
"io"
"net/http"
"os/exec"
"strings"
)
func systemctlReplace(out string) (bool, error) {
var (
err error
isReplace bool
)
if IsExists("/.dockerenv") && strings.Contains(out, "Failed to get D-Bus") {
isReplace = true
fmt.Println(Yellow("正在下载并替换适配的systemctl。。"))
if err = ExecCommand("curl -L https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl.py -o /usr/bin/systemctl && chmod +x /usr/bin/systemctl"); err != nil {
return isReplace, err
}
fmt.Println()
}
return isReplace, err
}
func systemctlBase(name, operate string) (string, error) {
out, err := exec.Command("bash", "-c", fmt.Sprintf("systemctl %s %s", operate, name)).CombinedOutput()
if v, _ := systemctlReplace(string(out)); v {
out, err = exec.Command("bash", "-c", fmt.Sprintf("systemctl %s %s", operate, name)).CombinedOutput()
}
return string(out), err
}
// SystemctlStart 服务启动
func SystemctlStart(name string) {
if _, err := systemctlBase(name, "start"); err != nil {
fmt.Println(Red(fmt.Sprintf("启动%s失败!", name)))
} else {
fmt.Println(Green(fmt.Sprintf("启动%s成功!", name)))
}
}
// SystemctlStop 服务停止
func SystemctlStop(name string) {
if _, err := systemctlBase(name, "stop"); err != nil {
fmt.Println(Red(fmt.Sprintf("停止%s失败!", name)))
} else {
fmt.Println(Green(fmt.Sprintf("停止%s成功!", name)))
}
}
// SystemctlRestart 服务重启
func SystemctlRestart(name string) {
if _, err := systemctlBase(name, "restart"); err != nil {
fmt.Println(Red(fmt.Sprintf("重启%s失败!", name)))
} else {
fmt.Println(Green(fmt.Sprintf("重启%s成功!", name)))
}
}
// SystemctlEnable 服务设置开机自启
func SystemctlEnable(name string) {
if _, err := systemctlBase(name, "enable"); err != nil {
fmt.Println(Red(fmt.Sprintf("设置%s开机自启失败!", name)))
}
}
// SystemctlStatus 服务状态查看
func SystemctlStatus(name string) string {
out, _ := systemctlBase(name, "status")
return out
}
// CheckCommandExists 检查命令是否存在
func CheckCommandExists(command string) bool {
if _, err := exec.LookPath(command); err != nil {
return false
}
return true
}
// RunWebShell 运行网上的脚本
func RunWebShell(webShellPath string) {
if !strings.HasPrefix(webShellPath, "http") && !strings.HasPrefix(webShellPath, "https") {
fmt.Printf("shell path must start with http or https!")
return
}
resp, err := http.Get(webShellPath)
if err != nil {
fmt.Println(err.Error())
}
defer resp.Body.Close()
installShell, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err.Error())
}
ExecCommand(string(installShell))
}
// ExecCommand 运行命令并实时查看运行结果
func ExecCommand(command string) error {
cmd := exec.Command("bash", "-c", command)
stdout, _ := cmd.StdoutPipe()
stderr, _ := cmd.StderrPipe()
if err := cmd.Start(); err != nil {
fmt.Println("Error:The command is err: ", err.Error())
return err
}
ch := make(chan string, 100)
stdoutScan := bufio.NewScanner(stdout)
stderrScan := bufio.NewScanner(stderr)
go func() {
for stdoutScan.Scan() {
line := stdoutScan.Text()
ch <- line
}
}()
go func() {
for stderrScan.Scan() {
line := stderrScan.Text()
ch <- line
}
}()
var err error
go func() {
err = cmd.Wait()
if err != nil && !strings.Contains(err.Error(), "exit status") {
fmt.Println("wait:", err.Error())
}
close(ch)
}()
for line := range ch {
fmt.Println(line)
}
return err
}
// ExecCommandWithResult 运行命令并获取结果
func ExecCommandWithResult(command string) string {
out, err := exec.Command("bash", "-c", command).CombinedOutput()
if strings.Contains(command, "systemctl") {
if v, _ := systemctlReplace(string(out)); v {
out, err = exec.Command("bash", "-c", command).CombinedOutput()
}
}
if err != nil && !strings.Contains(err.Error(), "exit status") {
fmt.Println("err: " + err.Error())
return ""
}
return string(out)
}
+121
View File
@@ -0,0 +1,121 @@
package util
import (
"bufio"
"fmt"
"io"
"math/rand"
"net"
"net/http"
"os"
"os/exec"
"strconv"
"time"
)
// PortIsUse 判断端口是否占用
func PortIsUse(port int) bool {
_, tcpError := net.DialTimeout("tcp", fmt.Sprintf(":%d", port), time.Millisecond*50)
udpAddr, _ := net.ResolveUDPAddr("udp4", fmt.Sprintf(":%d", port))
udpConn, udpError := net.ListenUDP("udp", udpAddr)
if udpConn != nil {
defer udpConn.Close()
}
return tcpError == nil || udpError != nil
}
// RandomPort 获取没占用的随机端口
func RandomPort() int {
for {
rand.New(rand.NewSource(time.Now().UnixNano()))
newPort := rand.Intn(65536)
if !PortIsUse(newPort) {
return newPort
}
}
}
// IsExists 检测指定路径文件或者文件夹是否存在
func IsExists(path string) bool {
_, err := os.Stat(path) //os.Stat获取文件信息
if err != nil {
if os.IsExist(err) {
return true
}
return false
}
return true
}
// GetLocalIP 获取本机ipv4地址
func GetLocalIP() string {
resp, err := http.Get("http://api.ipify.org")
if err != nil {
resp, _ = http.Get("http://icanhazip.com")
}
defer resp.Body.Close()
s, _ := io.ReadAll(resp.Body)
return string(s)
}
// InstallPack 安装指定名字软件
func InstallPack(name string) {
if !CheckCommandExists(name) {
if CheckCommandExists("yum") {
ExecCommand("yum install -y " + name)
} else if CheckCommandExists("apt-get") {
ExecCommand("apt-get update")
ExecCommand("apt-get install -y " + name)
}
}
}
// OpenPort 开通指定端口
func OpenPort(port int) {
if CheckCommandExists("firewall-cmd") {
ExecCommand(fmt.Sprintf("firewall-cmd --zone=public --add-port=%d/tcp --add-port=%d/udp --permanent >/dev/null 2>&1", port, port))
ExecCommand("firewall-cmd --reload >/dev/null 2>&1")
} else {
if len(ExecCommandWithResult(fmt.Sprintf(`iptables -nvL --line-number|grep -w "%d"`, port))) > 0 {
return
}
ExecCommand(fmt.Sprintf("iptables -I INPUT -p tcp --dport %d -j ACCEPT", port))
ExecCommand(fmt.Sprintf("iptables -I INPUT -p udp --dport %d -j ACCEPT", port))
ExecCommand(fmt.Sprintf("iptables -I OUTPUT -p udp --sport %d -j ACCEPT", port))
ExecCommand(fmt.Sprintf("iptables -I OUTPUT -p tcp --sport %d -j ACCEPT", port))
}
}
// Log 实时打印指定服务日志
func Log(serviceName string, line int) {
result, _ := LogChan(serviceName, "-n "+strconv.Itoa(line), make(chan byte))
for line := range result {
fmt.Println(line)
}
}
// LogChan 指定服务实时日志, 返回chan
func LogChan(serviceName, param string, closeChan chan byte) (chan string, error) {
cmd := exec.Command("bash", "-c", fmt.Sprintf("journalctl -f -u %s -o cat %s", serviceName, param))
stdout, _ := cmd.StdoutPipe()
if err := cmd.Start(); err != nil {
fmt.Println("Error:The command is err: ", err.Error())
return nil, err
}
ch := make(chan string, 100)
stdoutScan := bufio.NewScanner(stdout)
go func() {
for stdoutScan.Scan() {
select {
case <-closeChan:
stdout.Close()
return
default:
ch <- stdoutScan.Text()
}
}
}()
return ch, nil
}
+168
View File
@@ -0,0 +1,168 @@
package util
import (
"fmt"
"github.com/eiannone/keyboard"
"math/rand"
"reflect"
"regexp"
"strconv"
"time"
)
const (
// RED 红色
RED = "\033[31m"
// GREEN 绿色
GREEN = "\033[32m"
// YELLOW 黄色
YELLOW = "\033[33m"
// BLUE 蓝色
BLUE = "\033[34m"
// FUCHSIA 紫红色
FUCHSIA = "\033[35m"
// CYAN 青色
CYAN = "\033[36m"
// WHITE 白色
WHITE = "\033[37m"
// RESET 重置颜色
RESET = "\033[0m"
// LETTER 大小写英文字母常量
LETTER = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// DIGITS 数字常量
DIGITS = "0123456789"
// SPECIALS 特殊字符常量
SPECIALS = "~=+%^*/()[]{}/!@#$?|"
// ALL 全部字符常量
ALL = LETTER + DIGITS + SPECIALS
)
// IsInteger 判断字符串是否为整数
func IsInteger(input string) bool {
_, err := strconv.Atoi(input)
return err == nil
}
// RandString 随机字符串
func RandString(length int, source string) string {
var runes = []rune(source)
b := make([]rune, length)
rand.New(rand.NewSource(time.Now().UnixNano()))
for i := range b {
b[i] = runes[rand.Intn(len(runes))]
}
return string(b)
}
// VerifyEmailFormat 邮箱验证
func VerifyEmailFormat(email string) bool {
pattern := `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z]\.){1,4}[a-z]{2,4}$`
reg := regexp.MustCompile(pattern)
return reg.MatchString(email)
}
func getChar(str string) string {
err := keyboard.Open()
if err != nil {
panic(err)
}
defer keyboard.Close()
fmt.Print(str)
char, _, _ := keyboard.GetKey()
fmt.Printf("%c\n", char)
if char == 0 {
return ""
}
return string(char)
}
// LoopInput 循环输入选择, 或者直接回车退出
func LoopInput(tip string, choices interface{}, singleRowPrint bool) int {
reflectValue := reflect.ValueOf(choices)
if reflectValue.Kind() != reflect.Slice && reflectValue.Kind() != reflect.Array {
fmt.Println("only support slice or array type!")
return -1
}
length := reflectValue.Len()
if reflectValue.Type().String() == "[]string" {
if singleRowPrint {
for i := 0; i < length; i++ {
fmt.Printf("%d.%s\n\n", i+1, reflectValue.Index(i).Interface())
}
} else {
for i := 0; i < length; i++ {
if i%2 == 0 {
fmt.Printf("%d.%-15s\t", i+1, reflectValue.Index(i).Interface())
} else {
fmt.Printf("%d.%-15s\n\n", i+1, reflectValue.Index(i).Interface())
}
}
}
}
for {
inputString := ""
if length < 10 {
inputString = getChar(tip)
} else {
fmt.Print(tip)
_, _ = fmt.Scanln(&inputString)
}
if inputString == "" {
return -1
} else if !IsInteger(inputString) {
fmt.Println("输入有误,请重新输入")
continue
}
number, _ := strconv.Atoi(inputString)
if number <= length && number > 0 {
return number
}
fmt.Println("输入数字越界,请重新输入")
}
}
// Input 读取终端用户输入
func Input(tip string, defaultValue string) string {
input := ""
fmt.Print(tip)
_, _ = fmt.Scanln(&input)
if input == "" && defaultValue != "" {
input = defaultValue
}
return input
}
// Red 红色
func Red(str string) string {
return RED + str + RESET
}
// Green 绿色
func Green(str string) string {
return GREEN + str + RESET
}
// Yellow 黄色
func Yellow(str string) string {
return YELLOW + str + RESET
}
// Blue 蓝色
func Blue(str string) string {
return BLUE + str + RESET
}
// Fuchsia 紫红色
func Fuchsia(str string) string {
return FUCHSIA + str + RESET
}
// Cyan 青色
func Cyan(str string) string {
return CYAN + str + RESET
}
// White 白色
func White(str string) string {
return WHITE + str + RESET
}
+146
View File
@@ -0,0 +1,146 @@
package util
import (
"errors"
"fmt"
"github.com/gorilla/websocket"
"net/http"
"sync"
)
// http升级websocket协议的配置
var wsUpgrader = websocket.Upgrader{
// 允许所有CORS跨域请求
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// WsMessage websocket消息
type WsMessage struct {
MessageType int
Data []byte
}
// WsConnection 封装websocket连接
type WsConnection struct {
wsSocket *websocket.Conn // 底层websocket
inChan chan *WsMessage // 读取队列
outChan chan *WsMessage // 发送队列
mutex sync.Mutex // 避免重复关闭管道
isClosed bool
CloseChan chan byte // 关闭通知
}
// 读取协程
func (wsConn *WsConnection) wsReadLoop() {
var (
msgType int
data []byte
msg *WsMessage
err error
)
for {
// 读一个message
if msgType, data, err = wsConn.wsSocket.ReadMessage(); err != nil {
fmt.Println("Read error: " + err.Error())
goto CLOSED
}
msg = &WsMessage{
msgType,
data,
}
// 放入请求队列
select {
case wsConn.inChan <- msg:
if string(data) == "exit" {
goto CLOSED
}
case <-wsConn.CloseChan:
goto CLOSED
}
}
CLOSED:
wsConn.WsClose()
}
// 发送协程
func (wsConn *WsConnection) wsWriteLoop() {
var (
msg *WsMessage
err error
)
for {
select {
// 取一个应答
case msg = <-wsConn.outChan:
// 写给websocket
if err = wsConn.wsSocket.WriteMessage(msg.MessageType, msg.Data); err != nil {
fmt.Println(err)
goto CLOSED
}
case <-wsConn.CloseChan:
goto CLOSED
}
}
CLOSED:
wsConn.WsClose()
}
// InitWebsocket 初始化ws
func InitWebsocket(resp http.ResponseWriter, req *http.Request) (wsConn *WsConnection, err error) {
var (
wsSocket *websocket.Conn
)
// 应答客户端告知升级连接为websocket
if wsSocket, err = wsUpgrader.Upgrade(resp, req, nil); err != nil {
return
}
wsConn = &WsConnection{
wsSocket: wsSocket,
inChan: make(chan *WsMessage, 1000),
outChan: make(chan *WsMessage, 1000),
CloseChan: make(chan byte),
isClosed: false,
}
// 读协程
go wsConn.wsReadLoop()
// 写协程
go wsConn.wsWriteLoop()
return
}
// WsWrite 发送消息
func (wsConn *WsConnection) WsWrite(messageType int, data []byte) (err error) {
select {
case wsConn.outChan <- &WsMessage{messageType, data}:
case <-wsConn.CloseChan:
err = errors.New("websocket closed")
}
return
}
// WsRead 读取消息
func (wsConn *WsConnection) WsRead() (msg *WsMessage, err error) {
select {
case msg = <-wsConn.inChan:
return
case <-wsConn.CloseChan:
err = errors.New("websocket closed")
}
return
}
// WsClose 关闭连接
func (wsConn *WsConnection) WsClose() {
wsConn.wsSocket.Close()
wsConn.mutex.Lock()
defer wsConn.mutex.Unlock()
if !wsConn.isClosed {
wsConn.isClosed = true
close(wsConn.CloseChan)
}
}