Files
Spark/client/core/core.go
2022-10-09 21:50:02 +08:00

205 lines
4.1 KiB
Go

package core
import (
"Spark/client/common"
"Spark/client/config"
"Spark/modules"
"Spark/utils"
"encoding/hex"
"errors"
"net/http"
"os"
"os/exec"
"runtime"
"strings"
"time"
ws "github.com/gorilla/websocket"
"github.com/kataras/golog"
)
// simplified type of map
type smap map[string]any
var stop bool
var (
errNoSecretHeader = errors.New(`can not find secret header`)
)
func Start() {
for !stop {
var err error
if common.WSConn != nil {
common.Mutex.Lock()
common.WSConn.Close()
common.Mutex.Unlock()
}
common.Mutex.Lock()
common.WSConn, err = connectWS()
common.Mutex.Unlock()
if err != nil && !stop {
golog.Error(`Connection error: `, err)
<-time.After(3 * time.Second)
continue
}
err = reportWS(common.WSConn)
if err != nil && !stop {
golog.Error(`Register error: `, err)
<-time.After(3 * time.Second)
continue
}
checkUpdate(common.WSConn)
err = handleWS(common.WSConn)
if err != nil && !stop {
golog.Error(`Execution error: `, err)
<-time.After(3 * time.Second)
continue
}
}
}
func connectWS() (*common.Conn, error) {
wsConn, wsResp, err := ws.DefaultDialer.Dial(config.GetBaseURL(true)+`/ws`, http.Header{
`UUID`: []string{config.Config.UUID},
`Key`: []string{config.Config.Key},
})
if err != nil {
return nil, err
}
header, find := wsResp.Header[`Secret`]
if !find || len(header) == 0 {
return nil, errNoSecretHeader
}
secret, err := hex.DecodeString(header[0])
if err != nil {
return nil, err
}
return common.CreateConn(wsConn, secret), nil
}
func reportWS(wsConn *common.Conn) error {
device, err := GetDevice()
if err != nil {
return err
}
pack := modules.CommonPack{Act: `report`, Data: *device}
err = wsConn.SendPack(pack)
common.WSConn.SetWriteDeadline(time.Time{})
if err != nil {
return err
}
common.WSConn.SetReadDeadline(utils.Now.Add(5 * time.Second))
_, data, err := common.WSConn.ReadMessage()
common.WSConn.SetReadDeadline(time.Time{})
if err != nil {
return err
}
data, err = utils.Decrypt(data, common.WSConn.GetSecret())
if err != nil {
return err
}
err = utils.JSON.Unmarshal(data, &pack)
if err != nil {
return err
}
if pack.Code != 0 {
return errors.New(`${i18n|unknownError}`)
}
return nil
}
func checkUpdate(wsConn *common.Conn) error {
if len(config.COMMIT) == 0 {
return nil
}
resp, err := common.HTTP.R().
SetBody(config.ConfigBuffer).
SetQueryParam(`os`, runtime.GOOS).
SetQueryParam(`arch`, runtime.GOARCH).
SetQueryParam(`commit`, config.COMMIT).
SetHeader(`Secret`, wsConn.GetSecretHex()).
Send(`POST`, config.GetBaseURL(false)+`/api/client/update`)
if err != nil {
return err
}
if resp == nil {
return errors.New(`${i18n|unknownError}`)
}
if strings.HasPrefix(resp.GetContentType(), `application/octet-stream`) {
body := resp.Bytes()
if len(body) > 0 {
selfPath, err := os.Executable()
if err != nil {
selfPath = os.Args[0]
}
err = os.WriteFile(selfPath+`.tmp`, body, 0755)
if err != nil {
return err
}
cmd := exec.Command(selfPath+`.tmp`, `--update`)
err = cmd.Start()
if err != nil {
return err
}
stop = true
wsConn.Close()
os.Exit(0)
}
return nil
}
return nil
}
func handleWS(wsConn *common.Conn) error {
errCount := 0
for {
_, data, err := wsConn.ReadMessage()
if err != nil {
golog.Error(err)
return nil
}
data, err = utils.Decrypt(data, wsConn.GetSecret())
if err != nil {
golog.Error(err)
errCount++
if errCount > 3 {
break
}
continue
}
pack := modules.Packet{}
utils.JSON.Unmarshal(data, &pack)
if err != nil {
golog.Error(err)
errCount++
if errCount > 3 {
break
}
continue
}
errCount = 0
if pack.Data == nil {
pack.Data = smap{}
}
go handleAct(pack, wsConn)
}
wsConn.Close()
return nil
}
func handleAct(pack modules.Packet, wsConn *common.Conn) {
if act, ok := handlers[pack.Act]; !ok {
wsConn.SendCallback(modules.Packet{Code: 1, Msg: `${i18n|actionNotImplemented}`}, pack)
} else {
defer func() {
if r := recover(); r != nil {
golog.Error(`Panic: `, r)
}
}()
act(pack, wsConn)
}
}