2023-05-20 23:34:33 +08:00
|
|
|
package backend_golang
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
|
|
|
"bufio"
|
2023-05-29 09:39:16 +08:00
|
|
|
"embed"
|
2023-05-20 23:34:33 +08:00
|
|
|
"errors"
|
2023-11-03 21:18:42 +08:00
|
|
|
"fmt"
|
2023-05-20 23:34:33 +08:00
|
|
|
"io"
|
2023-05-29 09:39:16 +08:00
|
|
|
"io/fs"
|
2023-11-03 21:18:42 +08:00
|
|
|
"net"
|
2023-05-20 23:34:33 +08:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
2023-11-03 21:18:42 +08:00
|
|
|
"strconv"
|
2023-05-20 23:34:33 +08:00
|
|
|
"strings"
|
2023-12-12 22:13:09 +08:00
|
|
|
"syscall"
|
2023-05-20 23:34:33 +08:00
|
|
|
)
|
|
|
|
|
2023-12-12 22:13:09 +08:00
|
|
|
func CmdHelper(args ...string) (*exec.Cmd, error) {
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
return nil, errors.New("unsupported OS")
|
|
|
|
}
|
|
|
|
filename := "./cmd-helper.bat"
|
|
|
|
_, err := os.Stat(filename)
|
|
|
|
if err != nil {
|
|
|
|
if err := os.WriteFile(filename, []byte("start %*"), 0644); err != nil {
|
|
|
|
return nil, err
|
2023-05-21 10:09:27 +08:00
|
|
|
}
|
2023-12-12 22:13:09 +08:00
|
|
|
}
|
|
|
|
cmdHelper, err := filepath.Abs(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-06-01 16:54:21 +08:00
|
|
|
|
2023-12-12 22:13:09 +08:00
|
|
|
if strings.Contains(cmdHelper, " ") {
|
|
|
|
for _, arg := range args {
|
|
|
|
if strings.Contains(arg, " ") {
|
|
|
|
return nil, errors.New("path contains space") // golang bug https://github.com/golang/go/issues/17149#issuecomment-473976818
|
2023-06-02 23:35:33 +08:00
|
|
|
}
|
2023-06-01 16:54:21 +08:00
|
|
|
}
|
2023-12-12 22:13:09 +08:00
|
|
|
}
|
|
|
|
cmd := exec.Command(cmdHelper, args...)
|
|
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
|
|
//go:custom_build windows cmd.SysProcAttr.HideWindow = true
|
|
|
|
err = cmd.Start()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return cmd, nil
|
|
|
|
}
|
2023-06-01 16:54:21 +08:00
|
|
|
|
2023-12-12 22:13:09 +08:00
|
|
|
func Cmd(args ...string) (string, error) {
|
|
|
|
switch platform := runtime.GOOS; platform {
|
|
|
|
case "windows":
|
|
|
|
cmd, err := CmdHelper(args...)
|
2023-05-27 14:40:59 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2023-12-12 22:13:09 +08:00
|
|
|
cmd.Wait()
|
|
|
|
return "", nil
|
2023-06-01 16:54:21 +08:00
|
|
|
case "darwin":
|
|
|
|
ex, err := os.Executable()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
exDir := filepath.Dir(ex) + "/../../../"
|
2023-06-06 20:49:31 +08:00
|
|
|
cmd := exec.Command("osascript", "-e", `tell application "Terminal" to do script "`+"cd "+exDir+" && "+strings.Join(args, " ")+`"`)
|
2023-06-01 16:54:21 +08:00
|
|
|
err = cmd.Start()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
cmd.Wait()
|
|
|
|
return "", nil
|
|
|
|
case "linux":
|
2023-05-27 14:40:59 +08:00
|
|
|
cmd := exec.Command(args[0], args[1:]...)
|
|
|
|
err := cmd.Start()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
cmd.Wait()
|
|
|
|
return "", nil
|
2023-05-21 10:09:27 +08:00
|
|
|
}
|
2023-06-01 16:54:21 +08:00
|
|
|
return "", errors.New("unsupported OS")
|
2023-05-20 23:34:33 +08:00
|
|
|
}
|
|
|
|
|
2023-05-29 09:39:16 +08:00
|
|
|
func CopyEmbed(efs embed.FS) error {
|
2023-06-01 16:54:21 +08:00
|
|
|
prefix := ""
|
|
|
|
if runtime.GOOS == "darwin" {
|
|
|
|
ex, err := os.Executable()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
prefix = filepath.Dir(ex) + "/../../../"
|
|
|
|
}
|
|
|
|
|
2023-05-29 09:39:16 +08:00
|
|
|
err := fs.WalkDir(efs, ".", func(path string, d fs.DirEntry, err error) error {
|
|
|
|
if d.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
content, err := efs.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-06-01 16:54:21 +08:00
|
|
|
path = prefix + path
|
2023-05-29 09:39:16 +08:00
|
|
|
err = os.MkdirAll(path[:strings.LastIndex(path, "/")], 0755)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = os.WriteFile(path, content, 0644)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-05-20 23:34:33 +08:00
|
|
|
func GetPython() (string, error) {
|
|
|
|
switch platform := runtime.GOOS; platform {
|
|
|
|
case "windows":
|
|
|
|
_, err := os.Stat("py310/python.exe")
|
|
|
|
if err != nil {
|
|
|
|
_, err := os.Stat("python-3.10.11-embed-amd64.zip")
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.New("python zip not found")
|
|
|
|
} else {
|
|
|
|
err := Unzip("python-3.10.11-embed-amd64.zip", "py310")
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.New("failed to unzip python")
|
|
|
|
} else {
|
2023-06-15 21:57:54 +08:00
|
|
|
return "py310/python.exe", nil
|
2023-05-20 23:34:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2023-06-15 21:57:54 +08:00
|
|
|
return "py310/python.exe", nil
|
2023-05-20 23:34:33 +08:00
|
|
|
}
|
|
|
|
case "darwin":
|
|
|
|
return "python3", nil
|
|
|
|
case "linux":
|
|
|
|
return "python3", nil
|
|
|
|
}
|
|
|
|
return "", errors.New("unsupported OS")
|
|
|
|
}
|
|
|
|
|
|
|
|
func ChangeFileLine(filePath string, lineNumber int, newText string) error {
|
|
|
|
file, err := os.OpenFile(filePath, os.O_RDWR, 0644)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
content := make([]string, 0)
|
|
|
|
for scanner.Scan() {
|
|
|
|
content = append(content, scanner.Text())
|
|
|
|
}
|
|
|
|
|
|
|
|
content[lineNumber-1] = newText
|
|
|
|
|
|
|
|
newContent := strings.Join(content, "\n")
|
|
|
|
|
|
|
|
err = file.Truncate(0)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = file.Seek(0, io.SeekStart)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = file.WriteString(newContent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://gist.github.com/paulerickson/6d8650947ee4e3f3dbcc28fde10eaae7
|
|
|
|
func Unzip(source, destination string) error {
|
|
|
|
archive, err := zip.OpenReader(source)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer archive.Close()
|
|
|
|
for _, file := range archive.Reader.File {
|
|
|
|
reader, err := file.Open()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer reader.Close()
|
|
|
|
path := filepath.Join(destination, file.Name)
|
|
|
|
// Remove file if it already exists; no problem if it doesn't; other cases can error out below
|
|
|
|
_ = os.Remove(path)
|
|
|
|
// Create a directory at path, including parents
|
|
|
|
err = os.MkdirAll(path, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// If file is _supposed_ to be a directory, we're done
|
|
|
|
if file.FileInfo().IsDir() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// otherwise, remove that directory (_not_ including parents)
|
|
|
|
err = os.Remove(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// and create the actual file. This ensures that the parent directories exist!
|
|
|
|
// An archive may have a single file with a nested path, rather than a file for each parent dir
|
|
|
|
writer, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer writer.Close()
|
|
|
|
_, err = io.Copy(writer, reader)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2023-11-03 21:18:42 +08:00
|
|
|
|
|
|
|
func (a *App) IsPortAvailable(port int) bool {
|
|
|
|
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%s", strconv.Itoa(port)))
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
defer l.Close()
|
|
|
|
return true
|
|
|
|
}
|