mirror of https://gitee.com/godoos/godoos.git
7 changed files with 373 additions and 0 deletions
@ -0,0 +1,65 @@ |
|||||
|
package deps |
||||
|
|
||||
|
import ( |
||||
|
_ "embed" |
||||
|
"errors" |
||||
|
"godo/libs" |
||||
|
) |
||||
|
|
||||
|
//go:embed darwin/amd64.zip
|
||||
|
var embeddedDarwinAMD64Zip []byte |
||||
|
|
||||
|
//go:embed darwin/arm64.zip
|
||||
|
var embeddedDarwinARM64Zip []byte |
||||
|
|
||||
|
//go:embed linux/amd64.zip
|
||||
|
var embeddedLinuxAMD64Zip []byte |
||||
|
|
||||
|
//go:embed linux/arm64.zip
|
||||
|
var embeddedLinuxARM64Zip []byte |
||||
|
|
||||
|
//go:embed windows/amd64.zip
|
||||
|
var embeddedWindowsAMD64Zip []byte |
||||
|
|
||||
|
//go:embed windows/arm64.zip
|
||||
|
var embeddedWindowsARM64Zip []byte |
||||
|
|
||||
|
const ( |
||||
|
FRPCAPP = "frpc" |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
ErrUnsupportedOSArch = errors.New("unsupported OS and architecture combination") |
||||
|
embeddedZips = map[string]map[string]map[string][]byte{ |
||||
|
libs.OSDarwin: { |
||||
|
libs.ArchAMD64: {FRPCAPP: embeddedDarwinAMD64Zip}, |
||||
|
libs.ArchARM64: {FRPCAPP: embeddedDarwinARM64Zip}, |
||||
|
libs.ArchARM: {FRPCAPP: embeddedDarwinARM64Zip}, |
||||
|
}, |
||||
|
libs.OSWindows: { |
||||
|
libs.ArchAMD64: {FRPCAPP: embeddedWindowsAMD64Zip}, |
||||
|
libs.ArchARM64: {FRPCAPP: embeddedWindowsARM64Zip}, |
||||
|
libs.ArchARM: {FRPCAPP: embeddedWindowsARM64Zip}, |
||||
|
}, |
||||
|
libs.OSLinux: { |
||||
|
libs.ArchAMD64: {FRPCAPP: embeddedLinuxAMD64Zip}, |
||||
|
libs.ArchARM64: {FRPCAPP: embeddedLinuxARM64Zip}, |
||||
|
libs.ArchARM: {FRPCAPP: embeddedLinuxARM64Zip}, |
||||
|
}, |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
func ExtractZip(appName, targetDir string) error { |
||||
|
os, arch := libs.GetOSAndArch() |
||||
|
archMap, ok := embeddedZips[os] |
||||
|
if !ok { |
||||
|
return ErrUnsupportedOSArch |
||||
|
} |
||||
|
|
||||
|
zipData, ok := archMap[arch][appName] |
||||
|
if !ok { |
||||
|
return ErrUnsupportedOSArch |
||||
|
} |
||||
|
|
||||
|
return extractZip(zipData, targetDir) |
||||
|
} |
@ -0,0 +1,86 @@ |
|||||
|
package deps |
||||
|
|
||||
|
import ( |
||||
|
"archive/zip" |
||||
|
"bytes" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"log" |
||||
|
"os" |
||||
|
"path/filepath" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
// extractZip 解压嵌入的 zip 文件到目标目录
|
||||
|
func extractZip(zipData []byte, targetDir string) error { |
||||
|
log.Println("Extracting embedded ZIP file...") |
||||
|
// 使用内存缓冲区来读取嵌入的ZIP数据
|
||||
|
reader := bytes.NewReader(zipData) |
||||
|
zipReader, err := zip.NewReader(reader, int64(len(zipData))) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("failed to create zip reader: %v", err) |
||||
|
} |
||||
|
|
||||
|
// 遍历ZIP文件中的每个条目并解压
|
||||
|
for _, zipEntry := range zipReader.File { |
||||
|
// 忽略 __MACOSX 文件夹
|
||||
|
if strings.HasPrefix(zipEntry.Name, "__MACOSX") { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// 检查条目名称是否以"."开头,如果是,则跳过
|
||||
|
if strings.HasPrefix(zipEntry.Name, ".") { |
||||
|
fmt.Printf("Skipping hidden entry: %s\n", zipEntry.Name) |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// 去掉 ZIP 文件中的顶层目录(zip文件名本身)
|
||||
|
entryName := zipEntry.Name |
||||
|
if idx := strings.Index(entryName, "/"); idx != -1 { |
||||
|
entryName = entryName[idx+1:] // 去掉顶层目录
|
||||
|
} |
||||
|
|
||||
|
// 如果 entryName 为空,说明是顶层目录本身,跳过
|
||||
|
if entryName == "" { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// 构建解压后的文件或目录路径
|
||||
|
entryPath := filepath.Join(targetDir, entryName) |
||||
|
fmt.Println("Extracting:", entryName) |
||||
|
|
||||
|
// 如果是目录,则创建目录
|
||||
|
if zipEntry.FileInfo().IsDir() { |
||||
|
if err := os.MkdirAll(entryPath, zipEntry.Mode()); err != nil { |
||||
|
return fmt.Errorf("failed to create directory: %v", err) |
||||
|
} |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// 如果是文件,则解压文件
|
||||
|
zipFile, err := zipEntry.Open() |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("failed to open zip file entry: %v", err) |
||||
|
} |
||||
|
defer zipFile.Close() |
||||
|
|
||||
|
// 确保目标文件的父目录存在
|
||||
|
if err := os.MkdirAll(filepath.Dir(entryPath), 0755); err != nil { |
||||
|
return fmt.Errorf("failed to create parent directory: %v", err) |
||||
|
} |
||||
|
|
||||
|
dstFile, err := os.OpenFile(entryPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("failed to create destination file: %v", err) |
||||
|
} |
||||
|
defer dstFile.Close() |
||||
|
|
||||
|
if _, err := io.Copy(dstFile, zipFile); err != nil { |
||||
|
return fmt.Errorf("failed to copy content to destination file: %v", err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
log.Println("Embedded ZIP extracted to", targetDir) |
||||
|
|
||||
|
return nil |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
package libs |
||||
|
|
||||
|
import "runtime" |
||||
|
|
||||
|
const ( |
||||
|
OSDarwin = "macOS" |
||||
|
OSWindows = "Windows" |
||||
|
OSLinux = "Linux" |
||||
|
|
||||
|
ArchAMD64 = "x86_64" |
||||
|
ArchARM64 = "ARM64" |
||||
|
ArchARM = "ARM" |
||||
|
) |
||||
|
|
||||
|
// GetOSAndArch 返回当前操作系统的名称和架构的名称
|
||||
|
func GetOSAndArch() (osName, archName string) { |
||||
|
// 获取操作系统
|
||||
|
switch runtime.GOOS { |
||||
|
case "darwin": |
||||
|
osName = OSDarwin |
||||
|
case "windows": |
||||
|
osName = OSWindows |
||||
|
case "linux": |
||||
|
osName = OSLinux |
||||
|
default: |
||||
|
osName = "Unknown OS" |
||||
|
} |
||||
|
|
||||
|
// 获取架构
|
||||
|
switch runtime.GOARCH { |
||||
|
case "amd64": |
||||
|
archName = ArchAMD64 |
||||
|
case "arm64": |
||||
|
archName = ArchARM64 |
||||
|
case "arm": |
||||
|
archName = ArchARM |
||||
|
default: |
||||
|
archName = "Unknown Architecture" |
||||
|
} |
||||
|
|
||||
|
return osName, archName |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
//go:build !windows
|
||||
|
|
||||
|
package progress |
||||
|
|
||||
|
import ( |
||||
|
// 导入包
|
||||
|
"os/exec" |
||||
|
) |
||||
|
|
||||
|
func SetHideConsoleCursor(cmd *exec.Cmd) *exec.Cmd { |
||||
|
return cmd |
||||
|
} |
@ -0,0 +1,149 @@ |
|||||
|
package progress |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"godo/deps" |
||||
|
"godo/libs" |
||||
|
"log" |
||||
|
"os" |
||||
|
"os/exec" |
||||
|
"path/filepath" |
||||
|
"runtime" |
||||
|
"sync" |
||||
|
) |
||||
|
|
||||
|
type Process struct { |
||||
|
Name string |
||||
|
Running bool |
||||
|
ExitCode int |
||||
|
Cmd *exec.Cmd |
||||
|
} |
||||
|
|
||||
|
var ( |
||||
|
processesMu sync.RWMutex |
||||
|
processes = make(map[string]*Process) |
||||
|
) |
||||
|
|
||||
|
func RegisterProcess(name string, cmdstr *exec.Cmd) { |
||||
|
processesMu.Lock() |
||||
|
defer processesMu.Unlock() |
||||
|
processes[name] = &Process{ |
||||
|
Name: name, |
||||
|
Running: true, |
||||
|
Cmd: cmdstr, |
||||
|
} |
||||
|
} |
||||
|
func GetCmd(name string) *Process { |
||||
|
processesMu.Lock() |
||||
|
defer processesMu.Unlock() |
||||
|
return processes[name] |
||||
|
} |
||||
|
|
||||
|
// StartCmd 执行指定名称的脚本。
|
||||
|
// 参数:
|
||||
|
// name - 脚本的名称。
|
||||
|
// 返回值:
|
||||
|
// 返回可能遇到的错误,如果执行成功,则返回nil。
|
||||
|
func StartCmd(name string) error { |
||||
|
info, ok := processes[name] |
||||
|
if ok && info.Running { |
||||
|
return fmt.Errorf("process information for '%s' is runing", name) |
||||
|
} |
||||
|
|
||||
|
path := name |
||||
|
appName := name |
||||
|
scriptPath, err := libs.GetCmdPath(path, name) |
||||
|
if err != nil { |
||||
|
// 不存在,解压应用
|
||||
|
switch name { |
||||
|
case deps.FRPCAPP: |
||||
|
appName = deps.FRPCAPP |
||||
|
default: |
||||
|
return errors.New("app not found") |
||||
|
} |
||||
|
err = deps.ExtractZip(appName, libs.GetAppExecDir()) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("failed to extract zip file: %v", err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 设置并启动脚本执行命令
|
||||
|
var cmd *exec.Cmd |
||||
|
switch name { |
||||
|
case "frpc": |
||||
|
// 检查配置文件
|
||||
|
configPath := filepath.Join(libs.GetAppExecDir(), appName, "frpc.ini") |
||||
|
if !libs.PathExists(configPath) { |
||||
|
return fmt.Errorf("frpc config file not found") |
||||
|
} |
||||
|
|
||||
|
params := []string{ |
||||
|
"-c", |
||||
|
configPath, |
||||
|
} |
||||
|
cmd = exec.Command(scriptPath, params...) |
||||
|
default: |
||||
|
cmd = exec.Command(scriptPath) |
||||
|
} |
||||
|
|
||||
|
if runtime.GOOS == "windows" { |
||||
|
// 在Windows上,通过设置CreationFlags来隐藏窗口
|
||||
|
cmd = SetHideConsoleCursor(cmd) |
||||
|
} |
||||
|
go func() { |
||||
|
// 启动脚本命令并返回可能的错误
|
||||
|
if err := cmd.Start(); err != nil { |
||||
|
log.Printf("failed to start process %s: %v", name, err) |
||||
|
return |
||||
|
} |
||||
|
RegisterProcess(name, cmd) |
||||
|
// 等待命令完成
|
||||
|
if err := cmd.Wait(); err != nil { |
||||
|
log.Printf("command failed for %s: %v", name, err) |
||||
|
return |
||||
|
} else { |
||||
|
log.Printf("%s command completed successfully", name) |
||||
|
} |
||||
|
|
||||
|
// 命令完成后,更新进程信息
|
||||
|
processesMu.Lock() |
||||
|
defer processesMu.Unlock() |
||||
|
if p, ok := processes[name]; ok { |
||||
|
p.Running = false |
||||
|
p.ExitCode = cmd.ProcessState.ExitCode() |
||||
|
} |
||||
|
}() |
||||
|
return nil |
||||
|
} |
||||
|
func StopCmd(name string) error { |
||||
|
cmd, ok := processes[name] |
||||
|
if !ok { |
||||
|
return fmt.Errorf("process information for '%s' not found", name) |
||||
|
} |
||||
|
|
||||
|
// 停止进程并更新status
|
||||
|
if err := cmd.Cmd.Process.Kill(); err != nil { |
||||
|
return fmt.Errorf("failed to kill process %s: %v", name, err) |
||||
|
} |
||||
|
//delete(processes, name) // 更新status,表示进程已停止
|
||||
|
cmd.Running = false |
||||
|
return nil |
||||
|
} |
||||
|
func RestartCmd(name string) error { |
||||
|
if err := StopCmd(name); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return StartCmd(name) |
||||
|
} |
||||
|
func StopAllCmd() error { |
||||
|
processesMu.Lock() |
||||
|
defer processesMu.Unlock() |
||||
|
|
||||
|
for name, cmd := range processes { |
||||
|
if err := cmd.Cmd.Process.Signal(os.Interrupt); err != nil { |
||||
|
return fmt.Errorf("failed to stop process %s: %v", name, err) |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
//go:build windows
|
||||
|
// +build windows
|
||||
|
|
||||
|
package progress |
||||
|
|
||||
|
import ( |
||||
|
// 导入包
|
||||
|
"os/exec" |
||||
|
"syscall" |
||||
|
) |
||||
|
|
||||
|
func SetHideConsoleCursor(cmd *exec.Cmd) *exec.Cmd { |
||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} |
||||
|
return cmd |
||||
|
} |
Loading…
Reference in new issue