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