From 58277ba0b6751a395aa36682605286b7a803d7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= Date: Mon, 9 Dec 2019 16:02:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=8E=88=E6=9D=83?= =?UTF-8?q?=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/agent/main.go | 105 +++++++++++++++++-------- cmd/dashboard/controller/member_api.go | 4 +- cmd/dashboard/main.go | 7 ++ cmd/dashboard/rpc/rpc.go | 4 +- model/common.go | 2 + model/monitor.go | 10 +++ proto/nezha.proto | 4 +- resource/static/main.css | 42 ++++++++-- resource/static/main.js | 3 +- resource/template/page/home.html | 35 +++++++-- service/monitor/monitor.go | 21 +++++ service/rpc/auth.go | 35 ++++----- service/rpc/nezha.go | 12 ++- 13 files changed, 211 insertions(+), 73 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 4ab9b35..5a38d70 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "io" "log" "os" "time" @@ -10,7 +11,9 @@ import ( "github.com/spf13/cobra" "google.golang.org/grpc" + "github.com/p14yground/nezha/model" pb "github.com/p14yground/nezha/proto" + "github.com/p14yground/nezha/service/dao" "github.com/p14yground/nezha/service/monitor" "github.com/p14yground/nezha/service/rpc" ) @@ -25,51 +28,89 @@ var ( 啦啦啦,啦啦啦,我是 mjj 小行家`, Run: run, } - appKey string - appSecret string + clientID string + clientSecret string + debug bool ) func main() { - rootCmd.PersistentFlags().StringVarP(&appKey, "id", "i", "", "客户端ID") - rootCmd.PersistentFlags().StringVarP(&appSecret, "secret", "p", "", "客户端Secret") + rootCmd.PersistentFlags().StringVarP(&clientID, "id", "i", "", "客户端ID") + rootCmd.PersistentFlags().StringVarP(&clientSecret, "secret", "p", "", "客户端Secret") + rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "开启Debug") if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) } } +var endReport time.Time +var reporting bool +var client pb.NezhaServiceClient +var ctx = context.Background() + func run(cmd *cobra.Command, args []string) { + dao.Conf = &model.Config{ + Debug: debug, + } auth := rpc.AuthHandler{ - AppKey: appKey, - AppSecret: appSecret, + ClientID: clientID, + ClientSecret: clientSecret, } - conn, err := grpc.Dial(":5555", grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth)) - if err != nil { - panic(err) - } - defer conn.Close() - client := pb.NewNezhaServiceClient(conn) - ctx := context.Background() - - resp, err := client.Register(ctx, monitor.GetHost().PB()) - if err != nil { - log.Printf("client.Register err: %v", err) - } - log.Printf("Register resp: %s", resp) - - hc, err := client.Heartbeat(ctx, &pb.Beat{ - Timestamp: fmt.Sprintf("%v", time.Now()), - }) - if err != nil { - log.Printf("client.Register err: %v", err) - } - log.Printf("Register resp: %s", hc) - - for i := 0; i < 3; i++ { - resp, err := client.ReportState(ctx, monitor.GetState(3).PB()) + go reportState() + var err error + var conn *grpc.ClientConn + var hc pb.NezhaService_HeartbeatClient + for { + log.Println("Try to reconnect ...") + time.Sleep(time.Second * 5) + conn, err = grpc.Dial(":5555", grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth)) if err != nil { - log.Printf("client.ReportState err: %v", err) + log.Printf("grpc.Dial err: %v", err) + continue } - log.Printf("ReportState resp: %s", resp) + client = pb.NewNezhaServiceClient(conn) + // 第一步注册 + client.Register(ctx, monitor.GetHost().PB()) + hc, err = client.Heartbeat(ctx, &pb.Beat{ + Timestamp: fmt.Sprintf("%v", time.Now()), + }) + if err != nil { + log.Printf("client.Register err: %v", err) + continue + } + err = receiveCommand(hc) + log.Printf("receiveCommand exit to main: %v", err) + } +} + +func receiveCommand(hc pb.NezhaService_HeartbeatClient) error { + var err error + var action *pb.Command + defer log.Printf("receiveCommand exit %v %v => %v", time.Now(), action, err) + for { + action, err = hc.Recv() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + switch action.GetType() { + case model.MTReportState: + endReport = time.Now().Add(time.Second * 10) + default: + log.Printf("Unknown action: %v", action) + } + } +} + +func reportState() { + var err error + defer log.Printf("reportState exit %v %v => %v", endReport, time.Now(), err) + for { + if endReport.After(time.Now()) { + _, err = client.ReportState(ctx, monitor.GetState(0).PB()) + } + time.Sleep(time.Second) } } diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go index c1ecd43..ecce8ed 100644 --- a/cmd/dashboard/controller/member_api.go +++ b/cmd/dashboard/controller/member_api.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/naiba/com" + "github.com/patrickmn/go-cache" "github.com/p14yground/nezha/model" "github.com/p14yground/nezha/pkg/mygin" @@ -37,9 +38,9 @@ type serverForm struct { func (ma *memberAPI) addServer(c *gin.Context) { var sf serverForm + var s model.Server err := c.ShouldBindJSON(&sf) if err == nil { - var s model.Server s.Name = sf.Name s.Secret = com.MD5(fmt.Sprintf("%s%s%d", time.Now(), sf.Name, dao.Admin.ID)) s.Secret = s.Secret[:10] @@ -52,6 +53,7 @@ func (ma *memberAPI) addServer(c *gin.Context) { }) return } + dao.Cache.Set(fmt.Sprintf("%s%d%s", model.CtxKeyServer, s.ID, s.Secret), s, cache.NoExpiration) c.JSON(http.StatusOK, model.Response{ Code: http.StatusOK, }) diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go index 4b276de..6060d58 100644 --- a/cmd/dashboard/main.go +++ b/cmd/dashboard/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "time" "github.com/jinzhu/gorm" @@ -36,6 +37,12 @@ func init() { func initDB() { dao.DB.AutoMigrate(model.Server{}) + // load cache + var servers []model.Server + dao.DB.Find(&servers) + for _, s := range servers { + dao.Cache.Set(fmt.Sprintf("%s%d%s", model.CtxKeyServer, s.ID, s.Secret), s, cache.NoExpiration) + } } func main() { diff --git a/cmd/dashboard/rpc/rpc.go b/cmd/dashboard/rpc/rpc.go index 9e27c53..778b4ab 100644 --- a/cmd/dashboard/rpc/rpc.go +++ b/cmd/dashboard/rpc/rpc.go @@ -14,8 +14,8 @@ func ServeRPC() { server := grpc.NewServer() pb.RegisterNezhaServiceServer(server, &rpcService.NezhaHandler{ Auth: &rpcService.AuthHandler{ - AppKey: "naiba", - AppSecret: "123456", + ClientID: "naiba", + ClientSecret: "123456", }, }) listen, err := net.Listen("tcp", ":5555") diff --git a/model/common.go b/model/common.go index 4602bfb..c683d8f 100644 --- a/model/common.go +++ b/model/common.go @@ -7,6 +7,8 @@ const CtxKeyIsUserLogin = "ckiul" // CtxKeyOauth2State .. const CtxKeyOauth2State = "cko2s" +// CtxKeyServer .. +const CtxKeyServer = "cks" // Common .. type Common struct { diff --git a/model/monitor.go b/model/monitor.go index 32dfb53..d71a250 100644 --- a/model/monitor.go +++ b/model/monitor.go @@ -2,6 +2,12 @@ package model import pb "github.com/p14yground/nezha/proto" +const ( + _ = iota + // MTReportState .. + MTReportState +) + // State .. type State struct { CPU float64 @@ -39,6 +45,8 @@ type Host struct { Virtualization string Uptime string BootTime string + IP string + CountryCode string Version string } @@ -52,6 +60,8 @@ func (h *Host) PB() *pb.Host { Virtualization: h.Virtualization, Uptime: h.Uptime, BootTime: h.BootTime, + Ip: h.IP, + CountryCode: h.CountryCode, Version: h.Version, } } diff --git a/proto/nezha.proto b/proto/nezha.proto index 36892a2..9ccc509 100644 --- a/proto/nezha.proto +++ b/proto/nezha.proto @@ -16,7 +16,9 @@ message Host { string virtualization = 5; string uptime = 6; string boot_time = 7; - string version = 8; + string ip = 8; + string country_code = 9; + string version = 10; } message State { diff --git a/resource/static/main.css b/resource/static/main.css index 3b430ee..9332e56 100644 --- a/resource/static/main.css +++ b/resource/static/main.css @@ -4,8 +4,19 @@ } } +.nb-container { + padding-top: 75px; + min-height: 80%; +} + +.footer.segment { + margin-top: 40px !important; + padding-top: 40px; + padding-bottom: 40px; +} + .login-form { - height: 100%; + height: 82%; } .login-form .column { @@ -16,12 +27,29 @@ height: 100%; } -.nb-container { - margin-top: 75px; +.status.cards .header>.icon { + float: right; + margin-right: 0; } -.footer.segment { - margin-top: 40px !important; - padding-top: 40px; - padding-bottom: 40px; +.status.cards .wide.column { + padding-top: 0 !important; + padding-bottom: 0 !important; + height: 2rem !important; +} + +.status.cards .three.wide.column { + padding-right: 0 !important; +} + +.status.cards .wide.column:nth-child(1) { + margin-top: 1rem !important; +} + +.status.cards .wide.column:nth-child(2) { + margin-top: 1rem !important; +} + +.status.cards .description { + padding-bottom: 1rem !important; } \ No newline at end of file diff --git a/resource/static/main.js b/resource/static/main.js index b330e3b..599f36e 100644 --- a/resource/static/main.js +++ b/resource/static/main.js @@ -1,11 +1,12 @@ $('.ui.checkbox').checkbox(); +$('.yellow.info.circle.icon').popup(); const confirmBtn = $('.mini.confirm.modal .positive.button') function showConfirm(title, content, callFn, extData) { const modal = $('.mini.confirm.modal') modal.children('.header').text(title) modal.children('.content').text(content) - if (confirmBtn.hasClass('loading')) { + if (confirmBtn.hasCslass('loading')) { return false } modal.modal({ diff --git a/resource/template/page/home.html b/resource/template/page/home.html index f2d5d02..ee171f9 100644 --- a/resource/template/page/home.html +++ b/resource/template/page/home.html @@ -3,17 +3,40 @@ {{template "common/menu" .}}
-
+
{{range $server := .Servers}}
-
{{.Name}}
+
{{.Name}} + +
-
-
-
+
+
CPU
+
+
+
+
+
+
+
+
硬盘
+
+
+
+
+
+
+
+
内存
+
+
+
+
+
+
-
CPU
diff --git a/service/monitor/monitor.go b/service/monitor/monitor.go index 1519ac7..9dad741 100644 --- a/service/monitor/monitor.go +++ b/service/monitor/monitor.go @@ -1,7 +1,11 @@ package monitor import ( + "encoding/json" "fmt" + "io/ioutil" + "net/http" + "strings" "time" "github.com/shirou/gopsutil/cpu" @@ -13,6 +17,11 @@ import ( "github.com/p14yground/nezha/model" ) +type ipDotSbGeoIP struct { + CountryCode string + IP string +} + // GetHost .. func GetHost() *model.Host { hi, _ := host.Info() @@ -21,6 +30,16 @@ func GetHost() *model.Host { for i := 0; i < len(ci); i++ { cpus = append(cpus, fmt.Sprintf("%v-%vC%vT", ci[i].ModelName, ci[i].Cores, ci[i].Stepping)) } + ip := ipDotSbGeoIP{ + IP: "127.0.0.1", + CountryCode: "cn", + } + resp, err := http.Get("https://api.ip.sb/geoip") + if err == nil { + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + json.Unmarshal(body, &ip) + } return &model.Host{ Platform: hi.OS, PlatformVersion: hi.PlatformVersion, @@ -29,6 +48,8 @@ func GetHost() *model.Host { Virtualization: hi.VirtualizationSystem, Uptime: fmt.Sprintf("%v", hi.Uptime), BootTime: fmt.Sprintf("%v", hi.BootTime), + IP: ip.IP, + CountryCode: strings.ToLower(ip.CountryCode), } } diff --git a/service/rpc/auth.go b/service/rpc/auth.go index aeca03e..a5fc474 100644 --- a/service/rpc/auth.go +++ b/service/rpc/auth.go @@ -2,7 +2,10 @@ package rpc import ( "context" + "fmt" + "github.com/p14yground/nezha/model" + "github.com/p14yground/nezha/service/dao" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" @@ -10,51 +13,41 @@ import ( // AuthHandler .. type AuthHandler struct { - AppKey string - AppSecret string + ClientID string + ClientSecret string } // GetRequestMetadata .. func (a *AuthHandler) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - return map[string]string{"app_key": a.AppKey, "app_secret": a.AppSecret}, nil + return map[string]string{"app_key": a.ClientID, "app_secret": a.ClientSecret}, nil } // RequireTransportSecurity .. func (a *AuthHandler) RequireTransportSecurity() bool { - return false + return !dao.Conf.Debug } // Check .. func (a *AuthHandler) Check(ctx context.Context) error { md, ok := metadata.FromIncomingContext(ctx) if !ok { - return status.Errorf(codes.Unauthenticated, "metadata.FromIncomingContext err") + return status.Errorf(codes.Unauthenticated, "获取 metaData 失败") } var ( - AppKey string - AppSecret string + ClientID string + ClientSecret string ) if value, ok := md["app_key"]; ok { - AppKey = value[0] + ClientID = value[0] } if value, ok := md["app_secret"]; ok { - AppSecret = value[0] + ClientSecret = value[0] } - if AppKey != a.GetAppKey() || AppSecret != a.GetAppSecret() { - return status.Errorf(codes.Unauthenticated, "invalid token") + if _, ok := dao.Cache.Get(fmt.Sprintf("%s%s%s", model.CtxKeyServer, ClientID, ClientSecret)); !ok { + return status.Errorf(codes.Unauthenticated, "客户端认证失败") } return nil } - -// GetAppKey .. -func (a *AuthHandler) GetAppKey() string { - return a.AppKey -} - -// GetAppSecret .. -func (a *AuthHandler) GetAppSecret() string { - return a.AppSecret -} diff --git a/service/rpc/nezha.go b/service/rpc/nezha.go index 5a15f63..38b4b33 100644 --- a/service/rpc/nezha.go +++ b/service/rpc/nezha.go @@ -3,7 +3,9 @@ package rpc import ( "context" "fmt" + "log" + "github.com/p14yground/nezha/model" pb "github.com/p14yground/nezha/proto" ) @@ -23,11 +25,17 @@ func (s *NezhaHandler) ReportState(c context.Context, r *pb.State) (*pb.Receipt, // Heartbeat .. func (s *NezhaHandler) Heartbeat(r *pb.Beat, stream pb.NezhaService_HeartbeatServer) error { + defer log.Println("Heartbeat exit") if err := s.Auth.Check(stream.Context()); err != nil { return err } - fmt.Printf("ReportState receive: %s\n", r) - return nil + err := stream.Send(&pb.Command{ + Type: model.MTReportState, + }) + if err != nil { + log.Printf("Heartbeat stream.Send err:%v", err) + } + select {} } // Register ..