add basic desktop app

This commit is contained in:
c9s 2021-02-04 18:22:47 +08:00
parent 0cb2a3c452
commit 621321f5db
9 changed files with 188 additions and 23 deletions

View File

@ -37,16 +37,22 @@ docker:
docker build --build-arg GO_MOD_CACHE=_mod --tag yoanlin/bbgo .
bash -c "[[ -n $(DOCKER_TAG) ]] && docker tag yoanlin/bbgo yoanlin/bbgo:$(DOCKER_TAG)"
docker-push:
docker push yoanlin/bbgo
bash -c "[[ -n $(DOCKER_TAG) ]] && docker push yoanlin/bbgo:$(DOCKER_TAG)"
static:
static: pkged.go
(cd frontend && yarn export)
pkger
git commit pkged.go -m "update pkged static files"
desktop: build/BBGO.app/Contents/MacOS/bbgo-desktop static
mkdir -p $(dir $<)
go build -o $< ./cmd/bbgo-desktop
# bash desktop/build-darwin.sh
tools:
GO111MODULES=off go get github.com/markbates/pkger/cmd/pkger
.PHONY: dist migrations
.PHONY: dist migrations desktop

141
cmd/bbgo-desktop/main.go Normal file
View File

@ -0,0 +1,141 @@
package main
import (
"context"
"net"
"os"
"os/signal"
"runtime"
"github.com/zserge/lorca"
log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/cmd"
"github.com/c9s/bbgo/pkg/server"
)
func main() {
var args []string
if runtime.GOOS == "linux" {
args = append(args, "--class=bbgo")
}
args = append(args, "--class=bbgo")
// here allocate a chrome window with a blank page.
ui, err := lorca.New("", "", 800, 640, args...)
if err != nil {
log.WithError(err).Error("failed to initialize the window")
return
}
defer ui.Close()
// A simple way to know when UI is ready (uses body.onload event in JS)
ui.Bind("start", func() {
log.Println("lorca is ready")
})
// Create and bind Go object to the UI
// ui.Bind("counterAdd", c.Add)
// Load HTML.
// You may also use `data:text/html,<base64>` approach to load initial HTML,
// e.g: ui.Load("data:text/html," + url.PathEscape(html))
// TODO: load the loading page html
// find a free port for binding the server
ln, err := net.Listen("tcp", "127.0.0.1:9999")
if err != nil {
log.WithError(err).Error("can not bind listener")
return
}
defer ln.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
configFile := "bbgo.yaml"
var setup *server.Setup
var userConfig *bbgo.Config
_, err = os.Stat(configFile)
if os.IsNotExist(err) {
setup = &server.Setup{
Context: ctx,
Cancel: cancel,
Token: "",
}
userConfig = &bbgo.Config{
Notifications: nil,
Persistence: nil,
Sessions: nil,
ExchangeStrategies: nil,
}
} else {
userConfig, err = bbgo.Load(configFile, true)
if err != nil {
log.WithError(err).Error("can not load config file")
return
}
}
environ := bbgo.NewEnvironment()
trader := bbgo.NewTrader(environ)
// we could initialize the environment from the settings
if setup == nil {
if err := cmd.BootstrapEnvironment(ctx, environ, userConfig) ; err != nil {
log.WithError(err).Error("failed to bootstrap environment")
return
}
if err := cmd.ConfigureTrader(trader, userConfig) ; err != nil {
log.WithError(err).Error("failed to configure trader")
return
}
// for setup mode, we don't start the trader
trader.Subscribe()
if err := trader.Run(ctx); err != nil {
log.WithError(err).Error("failed to start trader")
return
}
}
go func() {
srv := &server.Server{
Config: userConfig,
Environ: environ,
Trader: trader,
OpenInBrowser: false,
Setup: setup,
}
if err := srv.RunWithListener(ctx, ln); err != nil {
log.WithError(err).Errorf("server error")
}
}()
baseURL := "http://" + ln.Addr().String()
go server.PingUntil(ctx, baseURL, func() {
if err := ui.Load(baseURL) ; err != nil {
log.WithError(err).Error("failed to load page")
}
})
// Wait until the interrupt signal arrives or browser window is closed
sigc := make(chan os.Signal)
signal.Notify(sigc, os.Interrupt)
select {
case <-sigc:
case <-ui.Done():
}
log.Println("exiting...")
}

View File

@ -1,17 +1,17 @@
#!/bin/sh
APP="BBGO.app"
APP_DIR=build/$APP
mkdir -p $APP/Contents/{MacOS,Resources}
go build -o $APP/Contents/MacOS/bbgo
mkdir -p $APP_DIR/Contents/{MacOS,Resources}
go build -o $APP_DIR/Contents/MacOS/bbgo-desktop ./cmd/bbgo-desktop
cat > $APP/Contents/Info.plist << EOF
cat > $APP_DIR/Contents/Info.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>bbgo</string>
<string>bbgo-desktop</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundleIdentifier</key>
@ -20,5 +20,5 @@ cat > $APP/Contents/Info.plist << EOF
</plist>
EOF
cp icons/icon.icns $APP/Contents/Resources/icon.icns
find $APP
cp -v desktop/icons/icon.icns $APP_DIR/Contents/Resources/icon.icns
find $APP_DIR

View File

@ -48,7 +48,7 @@ export function queryStrategies(cb) {
export function querySessions(cb) {
return axios.get(baseURL + '/api/sessions', {})
.then(response => {
cb(response.data.sessions)
cb(response.data.sessions || [])
});
}

View File

@ -44,10 +44,10 @@ export default function Home() {
React.useEffect(() => {
querySessions((sessions) => {
if (sessions.length == 0) {
push("/setup");
} else {
if (sessions && sessions.length > 0) {
setSessions(sessions)
} else {
push("/setup");
}
}).catch((err) => {
console.error(err);

1
go.mod
View File

@ -51,6 +51,7 @@ require (
github.com/ugorji/go v1.2.3 // indirect
github.com/valyala/fastjson v1.5.1
github.com/x-cray/logrus-prefixed-formatter v0.5.2
github.com/zserge/lorca v0.1.9
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect
golang.org/x/text v0.3.5 // indirect

3
go.sum
View File

@ -370,6 +370,8 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7V
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
github.com/zserge/lorca v0.1.9 h1:vbDdkqdp2/rmeg8GlyCewY2X8Z+b0s7BqWyIQL/gakc=
github.com/zserge/lorca v0.1.9/go.mod h1:bVmnIbIRlOcoV285KIRSe4bUABKi7R7384Ycuum6e4A=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -426,6 +428,7 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=

View File

@ -95,14 +95,9 @@ func runSetup(baseCtx context.Context, userConfig *bbgo.Config, enableApiServer
return nil
}
func runConfig(basectx context.Context, userConfig *bbgo.Config, enableApiServer bool) error {
ctx, cancelTrading := context.WithCancel(basectx)
defer cancelTrading()
environ := bbgo.NewEnvironment()
if viper.IsSet("mysql-url") {
dsn := viper.GetString("mysql-url")
func BootstrapEnvironment(ctx context.Context, environ *bbgo.Environment, userConfig *bbgo.Config) error {
if dsn, ok := os.LookupEnv("MYSQL_URL") ; ok {
if err := environ.ConfigureDatabase(ctx, dsn); err != nil {
return err
}
@ -230,8 +225,10 @@ func runConfig(basectx context.Context, userConfig *bbgo.Config, enableApiServer
}
}
trader := bbgo.NewTrader(environ)
return nil
}
func ConfigureTrader(trader *bbgo.Trader, userConfig *bbgo.Config) error {
if userConfig.RiskControls != nil {
trader.SetRiskControls(userConfig.RiskControls)
}
@ -262,6 +259,23 @@ func runConfig(basectx context.Context, userConfig *bbgo.Config, enableApiServer
}
}
return nil
}
func runConfig(basectx context.Context, userConfig *bbgo.Config, enableApiServer bool) error {
ctx, cancelTrading := context.WithCancel(basectx)
defer cancelTrading()
environ := bbgo.NewEnvironment()
if err := BootstrapEnvironment(ctx, environ, userConfig) ; err != nil {
return err
}
trader := bbgo.NewTrader(environ)
if err := ConfigureTrader(trader, userConfig) ; err != nil {
return err
}
trader.Subscribe()
if err := trader.Run(ctx); err != nil {

View File

@ -7,7 +7,7 @@ import (
"github.com/sirupsen/logrus"
)
func pingUntil(ctx context.Context, baseURL string, callback func()) {
func PingUntil(ctx context.Context, baseURL string, callback func()) {
pingURL := baseURL + "/api/ping"
timeout := time.NewTimer(time.Minute)
@ -37,7 +37,7 @@ func pingUntil(ctx context.Context, baseURL string, callback func()) {
func pingAndOpenURL(ctx context.Context, baseURL string) {
setupURL := baseURL + "/setup"
go pingUntil(ctx, baseURL, func() {
go PingUntil(ctx, baseURL, func() {
if err := openURL(setupURL); err != nil {
logrus.WithError(err).Errorf("can not call open command to open the web page")
}