feat: refactor browser extension communication to use IPC (#1130)

This commit is contained in:
Levi authored and GitHub committed 2025-10-12 21:17:07 +08:00
1 parent 75a8a3a9ac
commit 9a5680fb45
17 files changed
+353 -116

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
//go:build !windows
// +build !windows
package main
import (
"net"
"os"
"path/filepath"
)
func Dial() (net.Conn, error) {
// Get binary path
exe, err := os.Executable()
if err != nil {
return nil, err
}
return net.Dial("unix", filepath.Join(filepath.Dir(exe), "gopeed_host.sock"))
}
+11
View File
@@ -0,0 +1,11 @@
package main
import (
"net"
"github.com/Microsoft/go-winio"
)
func Dial() (net.Conn, error) {
return winio.DialPipe(`\\.\pipe\gopeed_host`, nil)
}
+69 -24
View File
@@ -1,22 +1,24 @@
package main
import (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"strings"
"time"
"github.com/pkg/browser"
"github.com/shirou/gopsutil/v4/process"
)
const identifier = "gopeed"
type Message struct {
Method string `json:"method"`
Meta map[string]any `json:"meta"`
Params json.RawMessage `json:"params"`
}
@@ -26,31 +28,74 @@ type Response struct {
Message string `json:"message,omitempty"`
}
var apiMap = map[string]func(params json.RawMessage) (data any, err error){
"ping": func(params json.RawMessage) (data any, err error) {
processes, err := process.Processes()
if err != nil {
return false, err
}
func check() (data bool, err error) {
conn, err := Dial()
if err != nil {
return false, err
}
defer conn.Close()
return true, nil
}
for _, p := range processes {
name, err := p.Name()
if err != nil {
continue
}
func wakeup(hidden bool) error {
running, _ := check()
if running {
return nil
}
if strings.Contains(strings.ToLower(name), strings.ToLower(identifier)) {
return true, nil
}
uri := "gopeed:"
if hidden {
uri = uri + "?hidden=true"
}
if err := browser.OpenURL(uri); err != nil {
return err
}
for i := 0; i < 10; i++ {
if ok, _ := check(); ok {
return nil
}
return false, nil
time.Sleep(1 * time.Second)
}
return fmt.Errorf("start gopeed failed")
}
var apiMap = map[string]func(message *Message) (data any, err error){
"ping": func(message *Message) (data any, err error) {
return check()
},
"create": func(params json.RawMessage) (data any, err error) {
var strParams string
if err = json.Unmarshal(params, &strParams); err != nil {
"create": func(message *Message) (data any, err error) {
buf, err := message.Params.MarshalJSON()
if err != nil {
return
}
err = browser.OpenURL(fmt.Sprintf("%s:///create?params=%s", identifier, strParams))
silent := false
if v, ok := message.Meta["silent"]; ok {
silent, _ = v.(bool)
}
if err := wakeup(silent); err != nil {
return nil, err
}
client := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return Dial()
},
},
Timeout: 10 * time.Second,
}
req, err := http.NewRequest("POST", "http://127.0.0.1/create", bytes.NewBuffer(buf))
if err != nil {
return
}
if message.Meta != nil {
metaJson, _ := json.Marshal(message.Meta)
req.Header.Set("X-Gopeed-Host-Meta", string(metaJson))
}
_, err = client.Do(req)
return
},
}
@@ -88,7 +133,7 @@ func main() {
var data any
var err error
if handler, ok := apiMap[message.Method]; ok {
data, err = handler(message.Params)
data, err = handler(&message)
} else {
err = errors.New("Unknown method: " + message.Method)
}
+5 -6
View File
@@ -5,7 +5,6 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"syscall"
"time"
@@ -19,6 +18,7 @@ func main() {
pid := flag.Int("pid", 0, "PID of the process to update")
updateChannel := flag.String("channel", "", "Update channel")
packagePath := flag.String("asset", "", "Path to the package asset")
exeDir := flag.String("exeDir", "", "Directory of the entry executable")
logPath := flag.String("log", "", "Log file path")
flag.Parse()
@@ -43,7 +43,7 @@ func main() {
restart bool
err error
)
if restart, err = update(*pid, *updateChannel, *packagePath); err != nil {
if restart, err = update(*pid, *updateChannel, *packagePath, *exeDir); err != nil {
log.Printf("Update failed: %v\n", err)
os.Exit(1)
}
@@ -61,7 +61,7 @@ func main() {
os.Exit(0)
}
func update(pid int, updateChannel, packagePath string) (restart bool, err error) {
func update(pid int, updateChannel, packagePath, exeDir string) (restart bool, err error) {
killSignalChan := make(chan any, 1)
go func() {
@@ -76,9 +76,8 @@ func update(pid int, updateChannel, packagePath string) (restart bool, err error
}
}()
appDir := filepath.Dir(os.Args[0])
log.Printf("Updating process updateChannel=%s packagePath=%s appDir=%s\n", updateChannel, packagePath, appDir)
if restart, err = install(killSignalChan, updateChannel, packagePath, appDir); err != nil {
log.Printf("Updating process updateChannel=%s packagePath=%s exeDir=%s\n", updateChannel, packagePath, exeDir)
if restart, err = install(killSignalChan, updateChannel, packagePath, exeDir); err != nil {
return false, errors.Wrap(err, "failed to install package")
}
+9 -5
View File
@@ -25,16 +25,20 @@ func installByDmg(killSignalChan chan<- any, packagePath, destDir string) error
mountPoint := ""
for _, line := range strings.Split(string(output), "\n") {
if strings.Contains(line, "/Volumes/") {
fields := strings.Fields(line)
if len(fields) > 2 {
mountPoint = fields[len(fields)-1]
// Find the /Volumes/ path in the line
// hdiutil output format: /dev/disk4s1 Apple_HFS /Volumes/Gopeed
// or with sequence number: /dev/disk4s1 Apple_HFS /Volumes/Gopeed 1
idx := strings.Index(line, "/Volumes/")
if idx != -1 {
// Extract everything from /Volumes/ onwards and trim whitespace
mountPoint = strings.TrimSpace(line[idx:])
break
}
}
}
if mountPoint == "" {
return fmt.Errorf("failed to get mount point")
return fmt.Errorf("failed to get mount point from hdiutil output: %s", string(output))
}
// Detach the mounted DMG
@@ -45,7 +49,7 @@ func installByDmg(killSignalChan chan<- any, packagePath, destDir string) error
return err
}
if len(matches) == 0 {
return fmt.Errorf("no .app found in dmg")
return fmt.Errorf("no .app found in dmg, mountPoint: %s", mountPoint)
}
killSignalChan <- nil