From 064bf2016bd4c8db2e17d503260f57f97253e64e Mon Sep 17 00:00:00 2001 From: GoEdgeLab Date: Tue, 20 Jul 2021 17:15:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=81=A2=E5=A4=8D=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/edge-admin/main.go | 19 +- go.mod | 5 +- go.sum | 25 +-- internal/apps/app_cmd.go | 76 +++---- internal/apps/file_others.go | 17 -- internal/apps/file_windows.go | 17 -- internal/apps/pid.go | 113 ----------- internal/configs/api_config.go | 8 +- internal/const/vars.go | 7 + internal/nodes/admin_node.go | 76 ++++--- internal/rpc/rpc_client.go | 4 + internal/tasks/task_sync_api_nodes.go | 3 +- internal/tasks/task_sync_cluster.go | 3 +- .../web/actions/default/recover/helper.go | 17 ++ internal/web/actions/default/recover/index.go | 17 ++ internal/web/actions/default/recover/init.go | 15 ++ .../actions/default/recover/updateHosts.go | 192 ++++++++++++++++++ .../actions/default/recover/validateApi.go | 153 ++++++++++++++ internal/web/actions/default/setup/index.go | 36 +--- internal/web/actions/default/setup/install.go | 9 +- .../web/actions/default/setup/validateApi.go | 5 +- .../web/actions/default/setup/validateDb.go | 22 +- internal/web/helpers/user_must_auth.go | 5 + internal/web/helpers/user_should_auth.go | 7 +- internal/web/import.go | 3 + web/views/@default/recover/@install.css | 54 +++++ web/views/@default/recover/@install.css.map | 1 + web/views/@default/recover/@install.less | 67 ++++++ web/views/@default/recover/index.css | 54 +++++ web/views/@default/recover/index.css.map | 1 + web/views/@default/recover/index.html | 138 +++++++++++++ web/views/@default/recover/index.js | 42 ++++ web/views/@default/recover/index.less | 2 + web/views/@default/setup/index.html | 20 +- 34 files changed, 948 insertions(+), 285 deletions(-) delete mode 100644 internal/apps/file_others.go delete mode 100644 internal/apps/file_windows.go delete mode 100644 internal/apps/pid.go create mode 100644 internal/const/vars.go create mode 100644 internal/web/actions/default/recover/helper.go create mode 100644 internal/web/actions/default/recover/index.go create mode 100644 internal/web/actions/default/recover/init.go create mode 100644 internal/web/actions/default/recover/updateHosts.go create mode 100644 internal/web/actions/default/recover/validateApi.go create mode 100644 web/views/@default/recover/@install.css create mode 100644 web/views/@default/recover/@install.css.map create mode 100644 web/views/@default/recover/@install.less create mode 100644 web/views/@default/recover/index.css create mode 100644 web/views/@default/recover/index.css.map create mode 100644 web/views/@default/recover/index.html create mode 100644 web/views/@default/recover/index.js create mode 100644 web/views/@default/recover/index.less diff --git a/cmd/edge-admin/main.go b/cmd/edge-admin/main.go index 1838ff8d..ab1813d8 100644 --- a/cmd/edge-admin/main.go +++ b/cmd/edge-admin/main.go @@ -8,20 +8,22 @@ import ( "github.com/TeaOSLab/EdgeAdmin/internal/nodes" _ "github.com/TeaOSLab/EdgeAdmin/internal/web" _ "github.com/iwind/TeaGo/bootstrap" + "github.com/iwind/gosock/pkg/gosock" ) func main() { app := apps.NewAppCmd(). Version(teaconst.Version). Product(teaconst.ProductName). - Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset]"). + Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset|recover]"). Option("-h", "show this help"). Option("-v", "show version"). Option("start", "start the service"). Option("stop", "stop the service"). Option("service", "register service into systemd"). Option("daemon", "start the service with daemon"). - Option("reset", "reset configs") + Option("reset", "reset configs"). + Option("recover", "enter recovery mode") app.On("daemon", func() { nodes.NewAdminNode().Daemon() @@ -42,6 +44,19 @@ func main() { } fmt.Println("done") }) + app.On("recover", func() { + sock := gosock.NewTmpSock(teaconst.ProcessName) + if !sock.IsListening() { + fmt.Println("[ERROR]the service not started yet, you should start the service first") + return + } + _, err := sock.Send(&gosock.Command{Code: "recover"}) + if err != nil { + fmt.Println("[ERROR]enter recovery mode failed: " + err.Error()) + return + } + fmt.Println("enter recovery mode successfully") + }) app.Run(func() { adminNode := nodes.NewAdminNode() adminNode.Run() diff --git a/go.mod b/go.mod index 570e3d20..bc5ac55f 100644 --- a/go.mod +++ b/go.mod @@ -9,11 +9,11 @@ require ( github.com/cespare/xxhash v1.1.0 github.com/go-sql-driver/mysql v1.5.0 github.com/go-yaml/yaml v2.1.0+incompatible - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.6 // indirect github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa + github.com/iwind/gosock v0.0.0-20210720054405-4030f271aa3f github.com/miekg/dns v1.1.35 - github.com/shirou/gopsutil v3.21.5+incompatible // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/tealeg/xlsx/v3 v3.2.3 github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 @@ -21,6 +21,5 @@ require ( golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced // indirect google.golang.org/grpc v1.38.0 - google.golang.org/protobuf v1.26.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 ) diff --git a/go.sum b/go.sum index 27765a94..2e5877ea 100644 --- a/go.sum +++ b/go.sum @@ -44,7 +44,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= @@ -55,7 +54,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= @@ -63,13 +61,13 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f h1:r2O8PONj/KiuZjJHVHn7KlCePUIjNtgAmvLfgRafQ8o= -github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc= -github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060 h1:qdLtK4PDXxk2vMKkTWl5Fl9xqYuRCukzWAgJbLHdfOo= github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc= github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa h1:woN88uEmRRUNFD7pRZEtX9heDcjFn0ClMxjF5ButKow= github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/iwind/gosock v0.0.0-20210706152215-08fe64b7a8a3 h1:BVHIjJU5RUX0grmvDtr3ZZ2z0hX+ideJRpg4NtBXZfs= +github.com/iwind/gosock v0.0.0-20210706152215-08fe64b7a8a3/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA= +github.com/iwind/gosock v0.0.0-20210720054405-4030f271aa3f h1:+mKLTd5tCCLXK+iY5Xjz58hau6qFv9chGqcZ4FWUiIs= +github.com/iwind/gosock v0.0.0-20210720054405-4030f271aa3f/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -80,11 +78,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -110,8 +106,6 @@ github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk= github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa/go.mod h1:Yjr3bdWaVWyME1kha7X0jsz3k2DgXNa1Pj3XGyUAbx8= -github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc= -github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= @@ -151,7 +145,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= @@ -159,8 +152,8 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -175,8 +168,6 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c= -golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -185,7 +176,6 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7s golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -203,15 +193,14 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus= google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= @@ -220,7 +209,6 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= @@ -232,7 +220,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= diff --git a/internal/apps/app_cmd.go b/internal/apps/app_cmd.go index 425aa4bb..d35030a6 100644 --- a/internal/apps/app_cmd.go +++ b/internal/apps/app_cmd.go @@ -2,17 +2,19 @@ package apps import ( "fmt" - "github.com/iwind/TeaGo/Tea" + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/iwind/TeaGo/logs" + "github.com/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" + "github.com/iwind/gosock/pkg/gosock" "os" "os/exec" "runtime" "strconv" - "syscall" "time" ) -// App命令帮助 +// AppCmd App命令帮助 type AppCmd struct { product string version string @@ -21,10 +23,14 @@ type AppCmd struct { appendStrings []string directives []*Directive + + sock *gosock.Sock } func NewAppCmd() *AppCmd { - return &AppCmd{} + return &AppCmd{ + sock: gosock.NewTmpSock(teaconst.ProcessName), + } } type CommandHelpOption struct { @@ -32,25 +38,25 @@ type CommandHelpOption struct { Description string } -// 产品 +// Product 产品 func (this *AppCmd) Product(product string) *AppCmd { this.product = product return this } -// 版本 +// Version 版本 func (this *AppCmd) Version(version string) *AppCmd { this.version = version return this } -// 使用方法 +// Usage 使用方法 func (this *AppCmd) Usage(usage string) *AppCmd { this.usage = usage return this } -// 选项 +// Option 选项 func (this *AppCmd) Option(code string, description string) *AppCmd { this.options = append(this.options, &CommandHelpOption{ Code: code, @@ -59,13 +65,13 @@ func (this *AppCmd) Option(code string, description string) *AppCmd { return this } -// 附加内容 +// Append 附加内容 func (this *AppCmd) Append(appendString string) *AppCmd { this.appendStrings = append(this.appendStrings, appendString) return this } -// 打印 +// Print 打印 func (this *AppCmd) Print() { fmt.Println(this.product + " v" + this.version) @@ -104,7 +110,7 @@ func (this *AppCmd) Print() { } } -// 添加指令 +// On 添加指令 func (this *AppCmd) On(arg string, callback func()) { this.directives = append(this.directives, &Directive{ Arg: arg, @@ -112,7 +118,7 @@ func (this *AppCmd) On(arg string, callback func()) { }) } -// 运行 +// Run 运行 func (this *AppCmd) Run(main func()) { // 获取参数 args := os.Args[1:] @@ -151,9 +157,6 @@ func (this *AppCmd) Run(main func()) { return } - // 记录PID - _ = this.writePid() - // 日志 writer := new(LogWriter) writer.Init() @@ -175,9 +178,9 @@ func (this *AppCmd) runHelp() { // 启动 func (this *AppCmd) runStart() { - proc := this.checkPid() - if proc != nil { - fmt.Println(this.product+" already started, pid:", proc.Pid) + var pid = this.getPID() + if pid > 0 { + fmt.Println(this.product+" already started, pid:", pid) return } @@ -193,18 +196,15 @@ func (this *AppCmd) runStart() { // 停止 func (this *AppCmd) runStop() { - proc := this.checkPid() - if proc == nil { + var pid = this.getPID() + if pid == 0 { fmt.Println(this.product + " not started yet") return } - // 停止进程 - _ = proc.Signal(syscall.SIGQUIT) + _, _ = this.sock.Send(&gosock.Command{Code: "stop"}) - // 在Windows上经常不能及时释放资源 - _ = DeletePid(Tea.Root + "/bin/pid") - fmt.Println(this.product+" stopped ok, pid:", proc.Pid) + fmt.Println(this.product+" stopped ok, pid:", types.String(pid)) } // 重启 @@ -216,20 +216,24 @@ func (this *AppCmd) runRestart() { // 状态 func (this *AppCmd) runStatus() { - proc := this.checkPid() - if proc == nil { + var pid = this.getPID() + if pid == 0 { fmt.Println(this.product + " not started yet") - } else { - fmt.Println(this.product + " is running, pid: " + fmt.Sprintf("%d", proc.Pid)) + return } + + fmt.Println(this.product + " is running, pid: " + types.String(pid)) } -// 检查PID -func (this *AppCmd) checkPid() *os.Process { - return CheckPid(Tea.Root + "/bin/pid") -} +// 获取当前的PID +func (this *AppCmd) getPID() int { + if !this.sock.IsListening() { + return 0 + } -// 写入PID -func (this *AppCmd) writePid() error { - return WritePid(Tea.Root + "/bin/pid") + reply, err := this.sock.Send(&gosock.Command{Code: "pid"}) + if err != nil { + return 0 + } + return maps.NewMap(reply.Params).GetInt("pid") } diff --git a/internal/apps/file_others.go b/internal/apps/file_others.go deleted file mode 100644 index 73bf2fec..00000000 --- a/internal/apps/file_others.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build !windows - -package apps - -import ( - "os" - "syscall" -) - -// lock file -func LockFile(fp *os.File) error { - return syscall.Flock(int(fp.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) -} - -func UnlockFile(fp *os.File) error { - return syscall.Flock(int(fp.Fd()), syscall.LOCK_UN) -} diff --git a/internal/apps/file_windows.go b/internal/apps/file_windows.go deleted file mode 100644 index fa2028f2..00000000 --- a/internal/apps/file_windows.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build windows - -package apps - -import ( - "errors" - "os" -) - -// lock file -func LockFile(fp *os.File) error { - return errors.New("not implemented on windows") -} - -func UnlockFile(fp *os.File) error { - return errors.New("not implemented on windows") -} diff --git a/internal/apps/pid.go b/internal/apps/pid.go deleted file mode 100644 index d5076ee3..00000000 --- a/internal/apps/pid.go +++ /dev/null @@ -1,113 +0,0 @@ -package apps - -import ( - "fmt" - "github.com/iwind/TeaGo/types" - "io/ioutil" - "os" - "runtime" -) - -var pidFileList = []*os.File{} - -// 检查Pid -func CheckPid(path string) *os.Process { - // windows上打开的文件是不能删除的 - if runtime.GOOS == "windows" { - if os.Remove(path) == nil { - return nil - } - } - - file, err := os.Open(path) - if err != nil { - return nil - } - - defer func() { - _ = file.Close() - }() - - // 是否能取得Lock - err = LockFile(file) - if err == nil { - _ = UnlockFile(file) - return nil - } - - pidBytes, err := ioutil.ReadAll(file) - if err != nil { - return nil - } - pid := types.Int(string(pidBytes)) - - if pid <= 0 { - return nil - } - - proc, _ := os.FindProcess(pid) - return proc -} - -// 写入Pid -func WritePid(path string) error { - fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666) - if err != nil { - return err - } - - if runtime.GOOS != "windows" { - err = LockFile(fp) - if err != nil { - return err - } - } - pidFileList = append(pidFileList, fp) // hold the file pointers - - _, err = fp.WriteString(fmt.Sprintf("%d", os.Getpid())) - if err != nil { - return err - } - - return nil -} - -// 写入Ppid -func WritePpid(path string) error { - fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666) - if err != nil { - return err - } - - if runtime.GOOS != "windows" { - err = LockFile(fp) - if err != nil { - return err - } - } - pidFileList = append(pidFileList, fp) // hold the file pointers - - _, err = fp.WriteString(fmt.Sprintf("%d", os.Getppid())) - if err != nil { - return err - } - - return nil -} - -// 删除Pid -func DeletePid(path string) error { - _, err := os.Stat(path) - if err != nil { - if !os.IsNotExist(err) { - return nil - } - return err - } - - for _, fp := range pidFileList { - _ = UnlockFile(fp) - _ = fp.Close() - } - return os.Remove(path) -} diff --git a/internal/configs/api_config.go b/internal/configs/api_config.go index a6e04e65..717afad8 100644 --- a/internal/configs/api_config.go +++ b/internal/configs/api_config.go @@ -108,7 +108,6 @@ func (this *APIConfig) WriteFile(path string) error { if err != nil { return err } - err = ioutil.WriteFile(path, data, 0666) // 写入 ~/ 和 /etc/ 目录,因为是备份需要,所以不需要提示错误信息 // 写入 ~/.edge-admin/ @@ -141,5 +140,10 @@ func (this *APIConfig) WriteFile(path string) error { } } - return err + err = ioutil.WriteFile(path, data, 0666) + if err != nil { + return err + } + + return nil } diff --git a/internal/const/vars.go b/internal/const/vars.go new file mode 100644 index 00000000..b5db6b39 --- /dev/null +++ b/internal/const/vars.go @@ -0,0 +1,7 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package teaconst + +var ( + IsRecoverMode = false +) diff --git a/internal/nodes/admin_node.go b/internal/nodes/admin_node.go index aa85806b..88a87e2e 100644 --- a/internal/nodes/admin_node.go +++ b/internal/nodes/admin_node.go @@ -10,11 +10,12 @@ import ( "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/logs" + "github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/rands" "github.com/iwind/TeaGo/sessions" + "github.com/iwind/gosock/pkg/gosock" "io/ioutil" "log" - "net" "os" "os/exec" "os/signal" @@ -25,6 +26,7 @@ import ( var SharedAdminNode *AdminNode = nil type AdminNode struct { + sock *gosock.Sock subPIDs []int } @@ -42,7 +44,7 @@ func (this *AdminNode) Run() { // 本地Sock err := this.listenSock() if err != nil { - logs.Println("NODE" + err.Error()) + logs.Println("[NODE]", err.Error()) return } @@ -89,11 +91,11 @@ func (this *AdminNode) Run() { // Daemon 实现守护进程 func (this *AdminNode) Daemon() { - path := os.TempDir() + "/edge-admin.sock" + var sock = gosock.NewTmpSock(teaconst.ProcessName) isDebug := lists.ContainsString(os.Args, "debug") isDebug = true for { - conn, err := net.DialTimeout("unix", path, 1*time.Second) + conn, err := sock.Dial() if err != nil { if isDebug { log.Println("[DAEMON]starting ...") @@ -281,37 +283,59 @@ func (this *AdminNode) genSecret() string { // 监听本地sock func (this *AdminNode) listenSock() error { - path := os.TempDir() + "/edge-admin.sock" + this.sock = gosock.NewTmpSock(teaconst.ProcessName) - // 检查是否已经存在 - _, err := os.Stat(path) - if err == nil { - conn, err := net.Dial("unix", path) - if err != nil { - _ = os.Remove(path) + // 检查是否在运行 + if this.sock.IsListening() { + reply, err := this.sock.Send(&gosock.Command{Code: "pid"}) + if err == nil { + return errors.New("error: the process is already running, pid: " + maps.NewMap(reply.Params).GetString("pid")) } else { - _ = conn.Close() + return errors.New("error: the process is already running") } } - // 新的监听任务 - listener, err := net.Listen("unix", path) - if err != nil { - return err - } - events.On(events.EventQuit, func() { - logs.Println("NODE", "quit unix sock") - _ = listener.Close() - }) - + // 启动监听 go func() { - for { - _, err := listener.Accept() - if err != nil { - return + this.sock.OnCommand(func(cmd *gosock.Command) { + switch cmd.Code { + case "pid": + _ = cmd.Reply(&gosock.Command{ + Code: "pid", + Params: map[string]interface{}{ + "pid": os.Getpid(), + }, + }) + case "stop": + _ = cmd.ReplyOk() + + // 关闭子进程 + for _, pid := range this.subPIDs { + p, err := os.FindProcess(pid) + if err == nil && p != nil { + _ = p.Kill() + } + } + + // 退出主进程 + events.Notify(events.EventQuit) + os.Exit(0) + case "recover": + teaconst.IsRecoverMode = true + _ = cmd.ReplyOk() } + }) + + err := this.sock.Listen() + if err != nil { + logs.Println("NODE", err.Error()) } }() + events.On(events.EventQuit, func() { + logs.Println("NODE", "quit unix sock") + _ = this.sock.Close() + }) + return nil } diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go index 211d6531..a5dd8c46 100644 --- a/internal/rpc/rpc_client.go +++ b/internal/rpc/rpc_client.go @@ -51,6 +51,10 @@ func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) { return client, nil } +func (this *RPCClient) APITokenRPC() pb.APITokenServiceClient { + return pb.NewAPITokenServiceClient(this.pickConn()) +} + func (this *RPCClient) AdminRPC() pb.AdminServiceClient { return pb.NewAdminServiceClient(this.pickConn()) } diff --git a/internal/tasks/task_sync_api_nodes.go b/internal/tasks/task_sync_api_nodes.go index 5909a0f2..329373c2 100644 --- a/internal/tasks/task_sync_api_nodes.go +++ b/internal/tasks/task_sync_api_nodes.go @@ -2,6 +2,7 @@ package tasks import ( "github.com/TeaOSLab/EdgeAdmin/internal/configs" + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/TeaOSLab/EdgeAdmin/internal/events" "github.com/TeaOSLab/EdgeAdmin/internal/rpc" "github.com/TeaOSLab/EdgeAdmin/internal/setup" @@ -44,7 +45,7 @@ func (this *SyncAPINodesTask) Start() { func (this *SyncAPINodesTask) Loop() error { // 如果还没有安装直接返回 - if !setup.IsConfigured() { + if !setup.IsConfigured() || teaconst.IsRecoverMode { return nil } diff --git a/internal/tasks/task_sync_cluster.go b/internal/tasks/task_sync_cluster.go index f1e2543c..aefbf202 100644 --- a/internal/tasks/task_sync_cluster.go +++ b/internal/tasks/task_sync_cluster.go @@ -1,6 +1,7 @@ package tasks import ( + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/TeaOSLab/EdgeAdmin/internal/events" "github.com/TeaOSLab/EdgeAdmin/internal/rpc" "github.com/TeaOSLab/EdgeAdmin/internal/setup" @@ -40,7 +41,7 @@ func (this *SyncClusterTask) Start() { func (this *SyncClusterTask) loop() error { // 如果还没有安装直接返回 - if !setup.IsConfigured() { + if !setup.IsConfigured() || teaconst.IsRecoverMode { return nil } diff --git a/internal/web/actions/default/recover/helper.go b/internal/web/actions/default/recover/helper.go new file mode 100644 index 00000000..d2b1db1e --- /dev/null +++ b/internal/web/actions/default/recover/helper.go @@ -0,0 +1,17 @@ +package recover + +import ( + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/iwind/TeaGo/actions" +) + +type Helper struct { +} + +func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) { + if !teaconst.IsRecoverMode { + actionPtr.Object().RedirectURL("/") + return false + } + return true +} diff --git a/internal/web/actions/default/recover/index.go b/internal/web/actions/default/recover/index.go new file mode 100644 index 00000000..f1be40b0 --- /dev/null +++ b/internal/web/actions/default/recover/index.go @@ -0,0 +1,17 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package recover + +import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "", "") +} + +func (this *IndexAction) RunGet(params struct{}) { + this.Show() +} diff --git a/internal/web/actions/default/recover/init.go b/internal/web/actions/default/recover/init.go new file mode 100644 index 00000000..e53f5245 --- /dev/null +++ b/internal/web/actions/default/recover/init.go @@ -0,0 +1,15 @@ +package recover + +import "github.com/iwind/TeaGo" + +func init() { + TeaGo.BeforeStart(func(server *TeaGo.Server) { + server. + Helper(new(Helper)). + Prefix("/recover"). + Get("", new(IndexAction)). + Post("/validateApi", new(ValidateApiAction)). + Post("/updateHosts", new(UpdateHostsAction)). + EndAll() + }) +} diff --git a/internal/web/actions/default/recover/updateHosts.go b/internal/web/actions/default/recover/updateHosts.go new file mode 100644 index 00000000..bab3ffef --- /dev/null +++ b/internal/web/actions/default/recover/updateHosts.go @@ -0,0 +1,192 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package recover + +import ( + "bytes" + "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/configs" + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/TeaOSLab/EdgeAdmin/internal/rpc" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/configutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/iwind/TeaGo/Tea" + "github.com/iwind/TeaGo/lists" +) + +type UpdateHostsAction struct { + actionutils.ParentAction +} + +func (this *UpdateHostsAction) RunPost(params struct { + Protocol string + Host string + Port string + NodeId string + NodeSecret string + + OldHosts []string + NewHosts []string +}) { + if len(params.OldHosts) != len(params.NewHosts) { + this.Fail("参数配置错误,请刷新页面后重试") + } + + client, err := rpc.NewRPCClient(&configs.APIConfig{ + RPC: struct { + Endpoints []string `yaml:"endpoints"` + }{ + Endpoints: []string{params.Protocol + "://" + configutils.QuoteIP(params.Host) + ":" + params.Port}, + }, + NodeId: params.NodeId, + Secret: params.NodeSecret, + }) + if err != nil { + this.FailField("host", "测试API节点时出错,请检查配置,错误信息:"+err.Error()) + } + _, err = client.APINodeRPC().FindCurrentAPINodeVersion(client.APIContext(0), &pb.FindCurrentAPINodeVersionRequest{}) + if err != nil { + this.FailField("host", "无法连接此API节点,错误信息:"+err.Error()) + } + + // 获取管理员节点信息 + apiTokensResp, err := client.APITokenRPC().FindAllEnabledAPITokens(client.APIContext(0), &pb.FindAllEnabledAPITokensRequest{Role: "admin"}) + if err != nil { + this.Fail("读取管理员令牌失败:" + err.Error()) + } + + var apiTokens = apiTokensResp.ApiTokens + if len(apiTokens) == 0 { + this.Fail("数据库中没有管理员令牌信息,请确认数据是否完整") + } + var adminAPIToken = apiTokens[0] + + // API节点列表 + nodesResp, err := client.APINodeRPC().FindAllEnabledAPINodes(client.Context(0), &pb.FindAllEnabledAPINodesRequest{}) + if err != nil { + this.Fail("获取API节点列表失败,错误信息:" + err.Error()) + } + var endpoints = []string{} + for _, node := range nodesResp.Nodes { + if !node.IsOn { + continue + } + + // http + if len(node.HttpJSON) > 0 { + for index, oldHost := range params.OldHosts { + if len(params.NewHosts[index]) == 0 { + continue + } + node.HttpJSON = bytes.ReplaceAll(node.HttpJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\"")) + } + } + + // https + if len(node.HttpsJSON) > 0 { + for index, oldHost := range params.OldHosts { + if len(params.NewHosts[index]) == 0 { + continue + } + node.HttpsJSON = bytes.ReplaceAll(node.HttpsJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\"")) + } + } + + // restHTTP + if len(node.RestHTTPJSON) > 0 { + for index, oldHost := range params.OldHosts { + if len(params.NewHosts[index]) == 0 { + continue + } + node.RestHTTPJSON = bytes.ReplaceAll(node.RestHTTPJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\"")) + } + } + + // restHTTPS + if len(node.RestHTTPSJSON) > 0 { + for index, oldHost := range params.OldHosts { + if len(params.NewHosts[index]) == 0 { + continue + } + node.RestHTTPSJSON = bytes.ReplaceAll(node.RestHTTPSJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\"")) + } + } + + // access addrs + if len(node.AccessAddrsJSON) > 0 { + for index, oldHost := range params.OldHosts { + if len(params.NewHosts[index]) == 0 { + continue + } + node.AccessAddrsJSON = bytes.ReplaceAll(node.AccessAddrsJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\"")) + } + + var addrs []*serverconfigs.NetworkAddressConfig + err = json.Unmarshal(node.AccessAddrsJSON, &addrs) + if err != nil { + this.Fail("读取节点访问地址失败:" + err.Error()) + } + for _, addr := range addrs { + err = addr.Init() + if err != nil { + // 暂时不提示错误 + continue + } + for _, a := range addr.FullAddresses() { + if !lists.ContainsString(endpoints, a) { + endpoints = append(endpoints, a) + } + } + } + } + + // 保存 + _, err = client.APINodeRPC().UpdateAPINode(client.Context(0), &pb.UpdateAPINodeRequest{ + NodeId: node.Id, + Name: node.Name, + Description: node.Description, + HttpJSON: node.HttpJSON, + HttpsJSON: node.HttpsJSON, + AccessAddrsJSON: node.AccessAddrsJSON, + IsOn: node.IsOn, + RestIsOn: node.RestIsOn, + RestHTTPJSON: node.RestHTTPJSON, + RestHTTPSJSON: node.RestHTTPSJSON, + }) + if err != nil { + this.Fail("保存API节点信息失败:" + err.Error()) + } + } + + // 修改api.yaml + var apiConfig = &configs.APIConfig{ + RPC: struct { + Endpoints []string `yaml:"endpoints"` + }{ + Endpoints: endpoints, + }, + NodeId: adminAPIToken.NodeId, + Secret: adminAPIToken.Secret, + } + err = apiConfig.WriteFile(Tea.Root + "/configs/api.yaml") + if err != nil { + this.Fail("保存configs/api.yaml失败:" + err.Error()) + } + + // 加载api.yaml + rpcClient, err := rpc.SharedRPC() + if err != nil { + this.Fail("初始化RPC失败:" + err.Error()) + } + err = rpcClient.UpdateConfig(apiConfig) + if err != nil { + this.Fail("修改API配置失败:" + err.Error()) + } + + // 退出恢复模式 + teaconst.IsRecoverMode = false + + this.Success() +} diff --git a/internal/web/actions/default/recover/validateApi.go b/internal/web/actions/default/recover/validateApi.go new file mode 100644 index 00000000..3ccfe13b --- /dev/null +++ b/internal/web/actions/default/recover/validateApi.go @@ -0,0 +1,153 @@ +package recover + +import ( + "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/configs" + "github.com/TeaOSLab/EdgeAdmin/internal/rpc" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/configutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/iwind/TeaGo/actions" + "github.com/iwind/TeaGo/lists" + "github.com/iwind/TeaGo/maps" + "strings" +) + +type ValidateApiAction struct { + actionutils.ParentAction +} + +func (this *ValidateApiAction) RunPost(params struct { + Protocol string + Host string + Port string + NodeId string + NodeSecret string + + Must *actions.Must +}) { + params.NodeId = strings.Trim(params.NodeId, "\"' ") + params.NodeSecret = strings.Trim(params.NodeSecret, "\"' ") + + // 使用已有的API节点 + params.Must. + Field("host", params.Host). + Require("请输入主机地址"). + Field("port", params.Port). + Require("请输入服务端口"). + Match(`^\d+$`, "服务端口只能是数字"). + Field("nodeId", params.NodeId). + Require("请输入节点nodeId"). + Field("nodeSecret", params.NodeSecret). + Require("请输入节点secret") + client, err := rpc.NewRPCClient(&configs.APIConfig{ + RPC: struct { + Endpoints []string `yaml:"endpoints"` + }{ + Endpoints: []string{params.Protocol + "://" + configutils.QuoteIP(params.Host) + ":" + params.Port}, + }, + NodeId: params.NodeId, + Secret: params.NodeSecret, + }) + if err != nil { + this.FailField("host", "测试API节点时出错,请检查配置,错误信息:"+err.Error()) + } + _, err = client.APINodeRPC().FindCurrentAPINodeVersion(client.APIContext(0), &pb.FindCurrentAPINodeVersionRequest{}) + if err != nil { + this.FailField("host", "无法连接此API节点,错误信息:"+err.Error()) + } + + // API节点列表 + nodesResp, err := client.APINodeRPC().FindAllEnabledAPINodes(client.Context(0), &pb.FindAllEnabledAPINodesRequest{}) + if err != nil { + this.Fail("获取API节点列表失败,错误信息:" + err.Error()) + } + var hosts = []string{} + for _, node := range nodesResp.Nodes { + if !node.IsOn { + continue + } + + // http + if len(node.HttpJSON) > 0 { + var config = &serverconfigs.HTTPProtocolConfig{} + err = json.Unmarshal(node.HttpJSON, config) + if err != nil { + this.Fail("读取节点HTTP信息失败:" + err.Error()) + } + for _, listen := range config.Listen { + if len(listen.Host) > 0 && !lists.ContainsString(hosts, listen.Host) { + hosts = append(hosts, listen.Host) + } + } + } + + // https + if len(node.HttpsJSON) > 0 { + var config = &serverconfigs.HTTPSProtocolConfig{} + err = json.Unmarshal(node.HttpsJSON, config) + if err != nil { + this.Fail("读取节点HTTPS信息失败:" + err.Error()) + } + for _, listen := range config.Listen { + if len(listen.Host) > 0 && !lists.ContainsString(hosts, listen.Host) { + hosts = append(hosts, listen.Host) + } + } + } + + // restHTTP + if len(node.RestHTTPJSON) > 0 { + var config = &serverconfigs.HTTPProtocolConfig{} + err = json.Unmarshal(node.RestHTTPJSON, config) + if err != nil { + this.Fail("读取节点REST HTTP信息失败:" + err.Error()) + } + for _, listen := range config.Listen { + if len(listen.Host) > 0 && !lists.ContainsString(hosts, listen.Host) { + hosts = append(hosts, listen.Host) + } + } + } + + // restHTTPS + if len(node.RestHTTPSJSON) > 0 { + var config = &serverconfigs.HTTPSProtocolConfig{} + err = json.Unmarshal(node.RestHTTPSJSON, config) + if err != nil { + this.Fail("读取节点REST HTTPS信息失败:" + err.Error()) + } + for _, listen := range config.Listen { + if len(listen.Host) > 0 && !lists.ContainsString(hosts, listen.Host) { + hosts = append(hosts, listen.Host) + } + } + } + + // access addrs + if len(node.AccessAddrsJSON) > 0 { + var addrs []*serverconfigs.NetworkAddressConfig + err = json.Unmarshal(node.AccessAddrsJSON, &addrs) + if err != nil { + this.Fail("读取节点访问地址失败:" + err.Error()) + } + for _, addr := range addrs { + if len(addr.Host) > 0 && !lists.ContainsString(hosts, addr.Host) { + hosts = append(hosts, addr.Host) + } + } + } + } + + this.Data["apiNode"] = maps.Map{ + "protocol": params.Protocol, + "host": params.Host, + "port": params.Port, + "nodeId": params.NodeId, + "nodeSecret": params.NodeSecret, + "hosts": hosts, + } + + this.Success() +} diff --git a/internal/web/actions/default/setup/index.go b/internal/web/actions/default/setup/index.go index f503aec0..062652d8 100644 --- a/internal/web/actions/default/setup/index.go +++ b/internal/web/actions/default/setup/index.go @@ -3,8 +3,7 @@ package setup import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "net" - "regexp" - "sort" + "strings" ) type IndexAction struct { @@ -16,31 +15,18 @@ func (this *IndexAction) Init() { } func (this *IndexAction) RunGet(params struct{}) { - // 当前服务器的IP - serverIPs := []string{} - addrs, _ := net.InterfaceAddrs() - for _, addr := range addrs { - netAddr, ok := addr.(*net.IPNet) - if !ok { - continue + var currentHost = this.Request.Host + if strings.Contains(this.Request.Host, ":") { + host, _, err := net.SplitHostPort(this.Request.Host) + if err == nil { + currentHost = host } - - serverIPs = append(serverIPs, netAddr.IP.String()) } - - // 对IP进行排序,我们希望IPv4排在前面,而且希望127.0.0.1排在IPv4里的最后 - sort.Slice(serverIPs, func(i, j int) bool { - ip1 := serverIPs[i] - - if ip1 == "127.0.0.1" { - return false - } - if regexp.MustCompile(`^\d+\.\d+\.\d+.\d+$`).MatchString(ip1) { - return true - } - return false - }) - this.Data["serverIPs"] = serverIPs + if net.ParseIP(currentHost) != nil { + this.Data["currentHost"] = currentHost + } else { + this.Data["currentHost"] = "" + } this.Show() } diff --git a/internal/web/actions/default/setup/install.go b/internal/web/actions/default/setup/install.go index bd83d076..fd71408b 100644 --- a/internal/web/actions/default/setup/install.go +++ b/internal/web/actions/default/setup/install.go @@ -7,6 +7,7 @@ import ( "github.com/TeaOSLab/EdgeAdmin/internal/nodes" "github.com/TeaOSLab/EdgeAdmin/internal/rpc" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/configutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs" "github.com/go-yaml/yaml" @@ -68,14 +69,14 @@ func (this *InstallAction) RunPost(params struct { _, err = os.Stat(apiNodeDir) if err != nil { if os.IsNotExist(err) { - this.Fail("在当前目录下找不到" + dir + "目录,请将" + dir + "目录上传或者重新下载解压") + this.Fail("在当前目录(" + Tea.Root + ")下找不到" + dir + "目录,请将" + dir + "目录上传或者重新下载解压") } this.Fail("无法检查" + dir + "目录,发生错误:" + err.Error()) } } // 保存数据库配置 - dsn := dbMap.GetString("username") + ":" + dbMap.GetString("password") + "@tcp(" + dbMap.GetString("host") + ":" + dbMap.GetString("port") + ")/" + dbMap.GetString("database") + "?charset=utf8mb4&timeout=30s" + 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": { @@ -175,7 +176,7 @@ func (this *InstallAction) RunPost(params struct { RPC: struct { Endpoints []string `yaml:"endpoints"` }{ - Endpoints: []string{"http://" + apiNodeMap.GetString("newHost") + ":" + apiNodeMap.GetString("newPort")}, + Endpoints: []string{"http://" + configutils.QuoteIP(apiNodeMap.GetString("newHost")) + ":" + apiNodeMap.GetString("newPort")}, }, NodeId: resultMap.GetString("adminNodeId"), Secret: resultMap.GetString("adminNodeSecret"), @@ -233,7 +234,7 @@ func (this *InstallAction) RunPost(params struct { RPC: struct { Endpoints []string `yaml:"endpoints"` }{ - Endpoints: []string{apiNodeMap.GetString("oldProtocol") + "://" + apiNodeMap.GetString("oldHost") + ":" + apiNodeMap.GetString("oldPort")}, + Endpoints: []string{apiNodeMap.GetString("oldProtocol") + "://" + configutils.QuoteIP(apiNodeMap.GetString("oldHost")) + ":" + apiNodeMap.GetString("oldPort")}, }, NodeId: apiNodeMap.GetString("oldNodeId"), Secret: apiNodeMap.GetString("oldNodeSecret"), diff --git a/internal/web/actions/default/setup/validateApi.go b/internal/web/actions/default/setup/validateApi.go index e5c51377..4005d229 100644 --- a/internal/web/actions/default/setup/validateApi.go +++ b/internal/web/actions/default/setup/validateApi.go @@ -4,6 +4,7 @@ import ( "github.com/TeaOSLab/EdgeAdmin/internal/configs" "github.com/TeaOSLab/EdgeAdmin/internal/rpc" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/configutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/actions" "github.com/iwind/TeaGo/maps" @@ -62,7 +63,7 @@ func (this *ValidateApiAction) RunPost(params struct { if net.ParseIP(params.NewHost) == nil { this.FailField("newHost", "请输入正确的节点主机地址") } - + params.Must. Field("newHost", params.NewHost). Require("请输入节点主机地址") @@ -86,7 +87,7 @@ func (this *ValidateApiAction) RunPost(params struct { RPC: struct { Endpoints []string `yaml:"endpoints"` }{ - Endpoints: []string{params.OldProtocol + "://" + params.OldHost + ":" + params.OldPort}, + Endpoints: []string{params.OldProtocol + "://" + configutils.QuoteIP(params.OldHost) + ":" + params.OldPort}, }, NodeId: params.OldNodeId, Secret: params.OldNodeSecret, diff --git a/internal/web/actions/default/setup/validateDb.go b/internal/web/actions/default/setup/validateDb.go index f2dc034d..fda18ba1 100644 --- a/internal/web/actions/default/setup/validateDb.go +++ b/internal/web/actions/default/setup/validateDb.go @@ -2,11 +2,14 @@ package setup import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/configutils" _ "github.com/go-sql-driver/mysql" "github.com/iwind/TeaGo/actions" "github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/maps" stringutil "github.com/iwind/TeaGo/utils/string" + "net" + "regexp" "strings" ) @@ -27,7 +30,20 @@ func (this *ValidateDbAction) RunPost(params struct { params.Must. Field("host", params.Host). Require("请输入主机地址"). - Match(`^[\w\.-]+$`, "主机地址中不能包含特殊字符"). + Expect(func() (message string, success bool) { + // 是否为IP + if net.ParseIP(params.Host) != nil { + success = true + return + } + if !regexp.MustCompile(`^[\w.-]+$`).MatchString(params.Host) { + message = "主机地址中不能包含特殊字符" + success = false + return + } + success = true + return + }). Field("port", params.Port). Require("请输入端口"). Match(`^\d+$`, "端口中只能包含数字"). @@ -41,7 +57,7 @@ func (this *ValidateDbAction) RunPost(params struct { // 测试连接 db, err := dbs.NewInstanceFromConfig(&dbs.DBConfig{ Driver: "mysql", - Dsn: params.Username + ":" + params.Password + "@tcp(" + params.Host + ":" + params.Port + ")/" + params.Database, + Dsn: params.Username + ":" + params.Password + "@tcp(" + configutils.QuoteIP(params.Host) + ":" + params.Port + ")/" + params.Database, Prefix: "", }) if err != nil { @@ -58,7 +74,7 @@ func (this *ValidateDbAction) RunPost(params struct { if strings.Contains(err.Error(), "Error 1049") { db, err := dbs.NewInstanceFromConfig(&dbs.DBConfig{ Driver: "mysql", - Dsn: params.Username + ":" + params.Password + "@tcp(" + params.Host + ":" + params.Port + ")/", + Dsn: params.Username + ":" + params.Password + "@tcp(" + configutils.QuoteIP(params.Host) + ":" + params.Port + ")/", Prefix: "", }) diff --git a/internal/web/helpers/user_must_auth.go b/internal/web/helpers/user_must_auth.go index cf14795c..28d71d56 100644 --- a/internal/web/helpers/user_must_auth.go +++ b/internal/web/helpers/user_must_auth.go @@ -21,6 +21,11 @@ func NewUserMustAuth(module string) *userMustAuth { } func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramName string) (goNext bool) { + if teaconst.IsRecoverMode { + actionPtr.Object().RedirectURL("/recover") + return false + } + var action = actionPtr.Object() // 安全相关 diff --git a/internal/web/helpers/user_should_auth.go b/internal/web/helpers/user_should_auth.go index 424b7081..1b88daa1 100644 --- a/internal/web/helpers/user_should_auth.go +++ b/internal/web/helpers/user_should_auth.go @@ -13,6 +13,11 @@ type UserShouldAuth struct { } func (this *UserShouldAuth) BeforeAction(actionPtr actions.ActionWrapper, paramName string) (goNext bool) { + if teaconst.IsRecoverMode { + actionPtr.Object().RedirectURL("/recover") + return false + } + this.action = actionPtr.Object() // 安全相关 @@ -34,7 +39,7 @@ func (this *UserShouldAuth) BeforeAction(actionPtr actions.ActionWrapper, paramN return true } -// 存储用户名到SESSION +// StoreAdmin 存储用户名到SESSION func (this *UserShouldAuth) StoreAdmin(adminId int64, remember bool) { // 修改sid的时间 if remember { diff --git a/internal/web/import.go b/internal/web/import.go index 288a70f2..86aad247 100644 --- a/internal/web/import.go +++ b/internal/web/import.go @@ -131,6 +131,9 @@ import ( _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-nodes" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui" + // 恢复 + _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/recover" + // 安装 _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/setup" diff --git a/web/views/@default/recover/@install.css b/web/views/@default/recover/@install.css new file mode 100644 index 00000000..df9ad6d3 --- /dev/null +++ b/web/views/@default/recover/@install.css @@ -0,0 +1,54 @@ +.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 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::-webkit-scrollbar { + width: 4px; +} +/*# sourceMappingURL=@install.css.map */ \ No newline at end of file diff --git a/web/views/@default/recover/@install.css.map b/web/views/@default/recover/@install.css.map new file mode 100644 index 00000000..92310f3d --- /dev/null +++ b/web/views/@default/recover/@install.css.map @@ -0,0 +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,MACC,GAAE;EACD,WAAA;;AArBH,YAyBC;EACC,iBAAA;;AA1BF,YAyBC,OAGC;EACC,0BAAA;EACA,2BAAA;;AA9BH,YAkCC;EACC,mBAAA;;AAnCF,YAsCC;EACC,gBAAA;EACA,eAAA;EACA,QAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,kBAAA;EACA,kBAAA;EACA,UAAA;;AA/CF,YAkDC,aAAY;EACX,UAAA;;AAnDF,YAsDC;EACC,eAAA;EACA,SAAA;EACA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,WAAA;;AAIF,YAAY;EACX,UAAA","file":"@install.css"} \ No newline at end of file diff --git a/web/views/@default/recover/@install.less b/web/views/@default/recover/@install.less new file mode 100644 index 00000000..261888b8 --- /dev/null +++ b/web/views/@default/recover/@install.less @@ -0,0 +1,67 @@ +.install-box { + @width: 50em; + + width: @width; + position: fixed; + left: 50%; + margin-left: -@width/2; + top: 1em; + bottom: 1em; + overflow-y: auto; + + .button.margin { + margin-top: 1em; + } + + .button.primary { + float: right; + } + + table { + td.title { + width: 10em; + } + } + + .radio { + margin-right: 1em; + + label { + cursor: pointer !important; + font-size: 0.9em !important; + } + } + + h3 { + font-weight: normal; + } + + .content-box { + overflow-y: auto; + position: fixed; + top: 5em; + bottom: 5em; + left: 50%; + width: @width; + padding-right: 1em; + margin-left: -@width/2; + z-index: 1; + } + + .content-box::-webkit-scrollbar { + width: 4px; + } + + .button-group { + position: fixed; + left: 50%; + margin-left: -@width/2; + z-index: 1; + width: @width; + bottom: 1em; + } +} + +.install-box::-webkit-scrollbar { + width: 4px; +} \ No newline at end of file diff --git a/web/views/@default/recover/index.css b/web/views/@default/recover/index.css new file mode 100644 index 00000000..0a598a60 --- /dev/null +++ b/web/views/@default/recover/index.css @@ -0,0 +1,54 @@ +.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 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::-webkit-scrollbar { + width: 4px; +} +/*# sourceMappingURL=index.css.map */ \ No newline at end of file diff --git a/web/views/@default/recover/index.css.map b/web/views/@default/recover/index.css.map new file mode 100644 index 00000000..4aef29ee --- /dev/null +++ b/web/views/@default/recover/index.css.map @@ -0,0 +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,MACC,GAAE;EACD,WAAA;;AArBH,YAyBC;EACC,iBAAA;;AA1BF,YAyBC,OAGC;EACC,0BAAA;EACA,2BAAA;;AA9BH,YAkCC;EACC,mBAAA;;AAnCF,YAsCC;EACC,gBAAA;EACA,eAAA;EACA,QAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,kBAAA;EACA,kBAAA;EACA,UAAA;;AA/CF,YAkDC,aAAY;EACX,UAAA;;AAnDF,YAsDC;EACC,eAAA;EACA,SAAA;EACA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,WAAA;;AAIF,YAAY;EACX,UAAA","file":"index.css"} \ No newline at end of file diff --git a/web/views/@default/recover/index.html b/web/views/@default/recover/index.html new file mode 100644 index 00000000..947f343d --- /dev/null +++ b/web/views/@default/recover/index.html @@ -0,0 +1,138 @@ + + + + + + GoEdge管理系统恢复模式 + + {$TEA.VUE} + {$TEA.SEMANTIC} + + + + + + + + +
+ +
+
+
+
1. 介绍
+
+
+
+
+
2. 新API地址
+
+
+
+
+
3. 修改API节点地址
+
+
+
+
+
4. 完成
+
+
+
+ + +
+ 可以通过恢复模式重新设置系统的API节点。 + + +
+ + +
+

输入一个可用的API节点信息:

+
+ + + + + + + + + + + + + + + + + + + + + +
节点协议 * + +

API节点使用的协议。

+
主机地址 * + +

API节点所在主机地址。

+
服务端口 * + +

API节点启动的端口。

+
节点nodeId * + +

在节点的配置文件中configs/api.yaml中获取,不需要带双引号。

+
节点secret * + +

在节点的配置文件中configs/api.yaml中获取,不需要带双引号。

+
+ +   + + +
+
+ + +
+
+ + + + + + + + + + + + + + + + + +
原地址新地址(留空表示不修改)
{{host}} + + +
+ +   + +
+
+ + +
+ 修改成功,请刷新此页面后,进入系统。 +
+
+ + + \ No newline at end of file diff --git a/web/views/@default/recover/index.js b/web/views/@default/recover/index.js new file mode 100644 index 00000000..96733f0f --- /dev/null +++ b/web/views/@default/recover/index.js @@ -0,0 +1,42 @@ +Tea.context(function () { + this.STEP_INTRO = "intro" + this.STEP_NEW_API = "newAPI" + this.STEP_API_LIST = "apiList" + this.STEP_FINISH = "finish" + + this.step = this.STEP_INTRO + + // 介绍 + this.goIntroNext = function () { + this.step = this.STEP_NEW_API + } + + // 新API地址 + this.apiNodeInfo = {} + this.apiRequesting = false + this.goBackIntro = function () { + this.step = this.STEP_INTRO + } + + this.apiSubmit = function () { + this.apiRequesting = true + } + + this.apiDone = function () { + this.apiRequesting = false + } + + this.apiSuccess = function (resp) { + this.step = this.STEP_API_LIST + this.apiNodeInfo = resp.data.apiNode + } + + // 修改API主机地址 + this.goBackAPI = function () { + this.step = this.STEP_NEW_API + } + + this.updateHostsSuccess = function () { + this.step = this.STEP_FINISH + } +}) \ No newline at end of file diff --git a/web/views/@default/recover/index.less b/web/views/@default/recover/index.less new file mode 100644 index 00000000..addf0a2a --- /dev/null +++ b/web/views/@default/recover/index.less @@ -0,0 +1,2 @@ +@import "@install"; + diff --git a/web/views/@default/setup/index.html b/web/views/@default/setup/index.html index fce5efbb..8e2b3af0 100644 --- a/web/views/@default/setup/index.html +++ b/web/views/@default/setup/index.html @@ -50,6 +50,7 @@
感谢你选择使用GoEdge集群服务系统,下面让我们一起开始配置系统。
在这之前如果你还没有可用的MySQL数据库,请先安装数据库再进行。
免责声明:GoEdge软件开发者并不对您的软件使用方法、服务对象、服务内容负道德或法律上的约束责任,在使用本软件时产生的一切法律风险自负。
+
用户协议:请在遵守中华人民共和国政策、法律、法规的前提下使用本软件;自愿承担因不当使用本软件产生的一切法律后果;承认GoEdge开发者拥有本软件的著作权;点击"开始"安装表示你同意此用户协议。
@@ -85,20 +86,10 @@
-
- -
-
- -
-
-
-

其他节点访问此API节点的主机地址,可以是IP或者域名,第一次安装时通常是当前服务器的IP地址。后期可以修改这个地址。

+

其他节点访问此API节点的主机地址,可以是IP或者域名,第一次安装时通常是当前服务器的公网IP地址。后期可以修改这个地址。

@@ -191,7 +182,10 @@ 访问日志保留天数 - +
+ + +

网站等服务记录的访问日志保留天数,防止无限制地占用数据库空间。