diff --git a/internal/web/actions/default/setup/install.go b/internal/web/actions/default/setup/install.go index d83fd6dd..2ca33b1f 100644 --- a/internal/web/actions/default/setup/install.go +++ b/internal/web/actions/default/setup/install.go @@ -17,13 +17,17 @@ import ( "github.com/iwind/TeaGo/maps" "github.com/iwind/gosock/pkg/gosock" "gopkg.in/yaml.v3" + "io" "os" "os/exec" + "strings" "time" ) type InstallAction struct { actionutils.ParentAction + + apiSetupFinished bool } func (this *InstallAction) RunPost(params struct { @@ -40,7 +44,7 @@ func (this *InstallAction) RunPost(params struct { // API节点配置 currentStatusText = "正在检查API节点配置" - apiNodeMap := maps.Map{} + var apiNodeMap = maps.Map{} err := json.Unmarshal(params.ApiNodeJSON, &apiNodeMap) if err != nil { this.Fail("API节点配置数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error()) @@ -48,7 +52,7 @@ func (this *InstallAction) RunPost(params struct { // 数据库 currentStatusText = "正在检查数据库配置" - dbMap := maps.Map{} + var dbMap = maps.Map{} err = json.Unmarshal(params.DbJSON, &dbMap) if err != nil { this.Fail("数据库配置数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error()) @@ -56,14 +60,14 @@ func (this *InstallAction) RunPost(params struct { // 管理员 currentStatusText = "正在检查管理员配置" - adminMap := maps.Map{} + var adminMap = maps.Map{} err = json.Unmarshal(params.AdminJSON, &adminMap) if err != nil { this.Fail("管理员数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error()) } // 安装API节点 - mode := apiNodeMap.GetString("mode") + var mode = apiNodeMap.GetString("mode") if mode == "new" { currentStatusText = "准备启动新API节点" @@ -74,7 +78,7 @@ func (this *InstallAction) RunPost(params struct { // ... // 检查环境 - apiNodeDir := Tea.Root + "/edge-api" + var apiNodeDir = Tea.Root + "/edge-api" for _, dir := range []string{"edge-api", "edge-api/configs", "edge-api/bin"} { apiNodeDir := Tea.Root + "/" + dir _, err = os.Stat(apiNodeDir) @@ -87,7 +91,7 @@ func (this *InstallAction) RunPost(params struct { } // 保存数据库配置 - dsn := dbMap.GetString("username") + ":" + dbMap.GetString("password") + "@tcp(" + configutils.QuoteIP(dbMap.GetString("host")) + ":" + dbMap.GetString("port") + ")/" + dbMap.GetString("database") + "?charset=utf8mb4&timeout=30s" + var dsn = dbMap.GetString("username") + ":" + dbMap.GetString("password") + "@tcp(" + configutils.QuoteIP(dbMap.GetString("host")) + ":" + dbMap.GetString("port") + ")/" + dbMap.GetString("database") + "?charset=utf8mb4&timeout=30s" dbConfig := &dbs.Config{ DBs: map[string]*dbs.DBConfig{ "prod": { @@ -108,7 +112,7 @@ func (this *InstallAction) RunPost(params struct { // 生成备份文件 homeDir, _ := os.UserHomeDir() - backupDirs := []string{"/etc/edge-api"} + var backupDirs = []string{"/etc/edge-api"} if len(homeDir) > 0 { backupDirs = append(backupDirs, homeDir+"/.edge-api") } @@ -151,15 +155,21 @@ func (this *InstallAction) RunPost(params struct { var resultMap = maps.Map{} logs.Println("[INSTALL]setup edge-api") { - cmd := exec.Command(apiNodeDir+"/bin/edge-api", "setup", "-api-node-protocol=http", "-api-node-host=\""+apiNodeMap.GetString("newHost")+"\"", "-api-node-port=\""+apiNodeMap.GetString("newPort")+"\"") - output := bytes.NewBuffer([]byte{}) + this.apiSetupFinished = false + var cmd = exec.Command(apiNodeDir+"/bin/edge-api", "setup", "-api-node-protocol=http", "-api-node-host=\""+apiNodeMap.GetString("newHost")+"\"", "-api-node-port=\""+apiNodeMap.GetString("newPort")+"\"") + var output = bytes.NewBuffer([]byte{}) cmd.Stdout = output + + // 试图读取执行日志 + go this.startReadingAPIInstallLog() + err = cmd.Run() + this.apiSetupFinished = true if err != nil { this.Fail("安装失败:" + err.Error()) } - resultData := output.Bytes() + var resultData = output.Bytes() err = json.Unmarshal(resultData, &resultMap) if err != nil { this.Fail("安装节点时返回数据错误:" + err.Error() + "(" + string(resultData) + ")") @@ -175,7 +185,7 @@ func (this *InstallAction) RunPost(params struct { // 关闭正在运行的API节点,防止冲突 logs.Println("[INSTALL]stop edge-api") { - cmd := exec.Command(apiNodeDir+"/bin/edge-api", "stop") + var cmd = exec.Command(apiNodeDir+"/bin/edge-api", "stop") _ = cmd.Run() } @@ -183,7 +193,7 @@ func (this *InstallAction) RunPost(params struct { currentStatusText = "正在启动API节点" logs.Println("[INSTALL]start edge-api") { - cmd := exec.Command(apiNodeDir + "/bin/edge-api") + var cmd = exec.Command(apiNodeDir + "/bin/edge-api") err = cmd.Start() if err != nil { this.Fail("API节点启动失败:" + err.Error()) @@ -220,7 +230,7 @@ func (this *InstallAction) RunPost(params struct { } // 写入API节点配置,完成安装 - apiConfig := &configs.APIConfig{ + var apiConfig = &configs.APIConfig{ RPC: struct { Endpoints []string `yaml:"endpoints"` DisableUpdate bool `yaml:"disableUpdate"` @@ -283,7 +293,7 @@ func (this *InstallAction) RunPost(params struct { this.Success() } else if mode == "old" { // 构造RPC - apiConfig := &configs.APIConfig{ + var apiConfig = &configs.APIConfig{ RPC: struct { Endpoints []string `yaml:"endpoints"` DisableUpdate bool `yaml:"disableUpdate"` @@ -303,7 +313,7 @@ func (this *InstallAction) RunPost(params struct { }() // 设置管理员 - ctx := client.APIContext(0) + var ctx = client.APIContext(0) _, err = client.AdminRPC().CreateOrUpdateAdmin(ctx, &pb.CreateOrUpdateAdminRequest{ Username: adminMap.GetString("username"), Password: adminMap.GetString("password"), @@ -344,3 +354,73 @@ func (this *InstallAction) RunPost(params struct { this.Fail("错误的API节点模式:'" + mode + "'") } } + +// 读取API安装时的日志,以便于显示当前正在执行的任务 +func (this *InstallAction) startReadingAPIInstallLog() { + var tmpDir = os.TempDir() + if len(tmpDir) == 0 { + return + } + var logFile = tmpDir + "/edge-install.log" + + var logFp *os.File + var err error + + // 尝试5秒钟 + for i := 0; i < 10; i++ { + logFp, err = os.Open(logFile) + if err != nil { + time.Sleep(1 * time.Second) + continue + } else { + break + } + } + if err != nil { + return + } + + if this.apiSetupFinished { + _ = logFp.Close() + return + } + + go func() { + defer func() { + _ = logFp.Close() + }() + + var ticker = time.NewTicker(1 * time.Second) + var logBuf = make([]byte, 256) + for range ticker.C { + if this.apiSetupFinished { + return + } + + _, err = logFp.Seek(-256, io.SeekEnd) + if err != nil { + currentStatusText = "" + return + } + + n, err := logFp.Read(logBuf) + if err != nil { + currentStatusText = "" + return + } + + if n > 0 { + var logData = string(logBuf[:n]) + var lines = strings.Split(logData, "\n") + if len(lines) >= 3 { + var line = strings.TrimSpace(lines[len(lines)-2]) + if len(line) > 0 { + if !this.apiSetupFinished { + currentStatusText = "正在执行 " + line + " ..." + } + } + } + } + } + }() +} diff --git a/web/views/@default/setup/@install.css b/web/views/@default/setup/@install.css index e69de29b..b60d0616 100644 --- a/web/views/@default/setup/@install.css +++ b/web/views/@default/setup/@install.css @@ -0,0 +1,69 @@ +.install-box { + width: 50em; + position: fixed; + left: 50%; + margin-left: -25em; + top: 1em; + bottom: 1em; + overflow-y: auto; +} +.install-box .button.margin { + margin-top: 1em; +} +.install-box .button.primary { + float: right; +} +.install-box .button.disabled { + float: right; +} +.install-box table td.title { + width: 10em; +} +.install-box .radio { + margin-right: 1em; +} +.install-box .radio label { + cursor: pointer !important; + font-size: 0.9em !important; +} +.install-box h3 { + font-weight: normal; +} +.install-box .content-box { + overflow-y: auto; + position: fixed; + top: 5em; + bottom: 5em; + left: 50%; + width: 50em; + padding-right: 1em; + margin-left: -25em; + z-index: 1; +} +.install-box .content-box::-webkit-scrollbar { + width: 4px; +} +.install-box .button-group { + position: fixed; + left: 50%; + margin-left: -25em; + z-index: 1; + width: 50em; + bottom: 1em; +} +.install-box .button-group button { + z-index: 10; +} +.install-box .button-group .status-box { + position: absolute; + top: 3em; + left: 5em; + right: 5em; + bottom: 0; + text-align: center; + z-index: 0; +} +.install-box::-webkit-scrollbar { + width: 4px; +} +/*# sourceMappingURL=@install.css.map */ \ No newline at end of file diff --git a/web/views/@default/setup/@install.css.map b/web/views/@default/setup/@install.css.map index 66dc9051..9ec79dcb 100644 --- a/web/views/@default/setup/@install.css.map +++ b/web/views/@default/setup/@install.css.map @@ -1 +1 @@ -undefined \ No newline at end of file +{"version":3,"sources":["@install.less"],"names":[],"mappings":"AAAA;EAIC,WAAA;EACA,eAAA;EACA,SAAA;EACA,kBAAA;EACA,QAAA;EACA,WAAA;EACA,gBAAA;;AAVD,YAYC,QAAO;EACN,eAAA;;AAbF,YAgBC,QAAO;EACN,YAAA;;AAjBF,YAoBC,QAAO;EACN,YAAA;;AArBF,YAwBC,MACC,GAAE;EACD,WAAA;;AA1BH,YA8BC;EACC,iBAAA;;AA/BF,YA8BC,OAGC;EACC,0BAAA;EACA,gBAAA;;AAnCH,YAuCC;EACC,mBAAA;;AAxCF,YA2CC;EACC,gBAAA;EACA,eAAA;EACA,QAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,kBAAA;EACA,kBAAA;EACA,UAAA;;AApDF,YAuDC,aAAY;EACX,UAAA;;AAxDF,YA2DC;EACC,eAAA;EACA,SAAA;EACA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,WAAA;;AAjEF,YA2DC,cAQC;EACC,WAAA;;AApEH,YA2DC,cAYC;EACC,kBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,SAAA;EACA,kBAAA;EACA,UAAA;;AAKH,YAAY;EACX,UAAA","file":"@install.css"} \ No newline at end of file diff --git a/web/views/@default/setup/@install.less b/web/views/@default/setup/@install.less index 5b33ac47..176224f7 100644 --- a/web/views/@default/setup/@install.less +++ b/web/views/@default/setup/@install.less @@ -1,10 +1,11 @@ .install-box { @width: 50em; + @half-width: 25em; width: @width; position: fixed; left: 50%; - margin-left: -@width/2; + margin-left: -@half-width; top: 1em; bottom: 1em; overflow-y: auto; @@ -48,7 +49,7 @@ left: 50%; width: @width; padding-right: 1em; - margin-left: -@width/2; + margin-left: -@half-width; z-index: 1; } @@ -59,7 +60,7 @@ .button-group { position: fixed; left: 50%; - margin-left: -@width/2; + margin-left: -@half-width; z-index: 1; width: @width; bottom: 1em; @@ -71,8 +72,8 @@ .status-box { position: absolute; top: 3em; - left: 15em; - right: 15em; + left: 5em; + right: 5em; bottom: 0; text-align: center; z-index: 0; diff --git a/web/views/@default/setup/index.css b/web/views/@default/setup/index.css index f5584d3b..51126112 100644 --- a/web/views/@default/setup/index.css +++ b/web/views/@default/setup/index.css @@ -57,8 +57,8 @@ .install-box .button-group .status-box { position: absolute; top: 3em; - left: 15em; - right: 15em; + left: 5em; + right: 5em; bottom: 0; text-align: center; z-index: 0; diff --git a/web/views/@default/setup/index.css.map b/web/views/@default/setup/index.css.map index 1d3ae2f6..09f3eb4e 100644 --- a/web/views/@default/setup/index.css.map +++ b/web/views/@default/setup/index.css.map @@ -1 +1 @@ -{"version":3,"sources":["@install.less"],"names":[],"mappings":"AAAA;EAGC,WAAA;EACA,eAAA;EACA,SAAA;EACA,kBAAA;EACA,QAAA;EACA,WAAA;EACA,gBAAA;;AATD,YAWC,QAAO;EACN,eAAA;;AAZF,YAeC,QAAO;EACN,YAAA;;AAhBF,YAmBC,QAAO;EACN,YAAA;;AApBF,YAuBC,MACC,GAAE;EACD,WAAA;;AAzBH,YA6BC;EACC,iBAAA;;AA9BF,YA6BC,OAGC;EACC,0BAAA;EACA,2BAAA;;AAlCH,YAsCC;EACC,mBAAA;;AAvCF,YA0CC;EACC,gBAAA;EACA,eAAA;EACA,QAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,kBAAA;EACA,kBAAA;EACA,UAAA;;AAnDF,YAsDC,aAAY;EACX,UAAA;;AAvDF,YA0DC;EACC,eAAA;EACA,SAAA;EACA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,WAAA;;AAhEF,YA0DC,cAQC;EACC,WAAA;;AAnEH,YA0DC,cAYC;EACC,kBAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EACA,SAAA;EACA,kBAAA;EACA,UAAA;;AAKH,YAAY;EACX,UAAA","file":"index.css"} \ No newline at end of file +{"version":3,"sources":["@install.less"],"names":[],"mappings":"AAAA;EAIC,WAAA;EACA,eAAA;EACA,SAAA;EACA,kBAAA;EACA,QAAA;EACA,WAAA;EACA,gBAAA;;AAVD,YAYC,QAAO;EACN,eAAA;;AAbF,YAgBC,QAAO;EACN,YAAA;;AAjBF,YAoBC,QAAO;EACN,YAAA;;AArBF,YAwBC,MACC,GAAE;EACD,WAAA;;AA1BH,YA8BC;EACC,iBAAA;;AA/BF,YA8BC,OAGC;EACC,0BAAA;EACA,gBAAA;;AAnCH,YAuCC;EACC,mBAAA;;AAxCF,YA2CC;EACC,gBAAA;EACA,eAAA;EACA,QAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,kBAAA;EACA,kBAAA;EACA,UAAA;;AApDF,YAuDC,aAAY;EACX,UAAA;;AAxDF,YA2DC;EACC,eAAA;EACA,SAAA;EACA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,WAAA;;AAjEF,YA2DC,cAQC;EACC,WAAA;;AApEH,YA2DC,cAYC;EACC,kBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,SAAA;EACA,kBAAA;EACA,UAAA;;AAKH,YAAY;EACX,UAAA","file":"index.css"} \ No newline at end of file diff --git a/web/views/@default/setup/index.less b/web/views/@default/setup/index.less index addf0a2a..d2f72e89 100644 --- a/web/views/@default/setup/index.less +++ b/web/views/@default/setup/index.less @@ -1,2 +1 @@ -@import "@install"; - +@import "@install"; \ No newline at end of file