From d8c43646539e9178b3053518381cf7ed9e4479ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= Date: Sun, 8 Dec 2019 16:59:58 +0800 Subject: [PATCH] =?UTF-8?q?Web=20=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + cmd/agent/main.go | 4 +- cmd/dashboard/controller/common_page.go | 32 +++ cmd/dashboard/controller/controller.go | 51 +++++ cmd/dashboard/controller/guest_page.go | 45 ++++ cmd/dashboard/controller/member_api.go | 57 +++++ cmd/dashboard/controller/member_page.go | 21 ++ cmd/dashboard/controller/oauth2.go | 83 ++++++++ cmd/dashboard/main.go | 41 ++-- cmd/dashboard/rpc/rpc.go | 26 +++ go.mod | 10 +- go.sum | 231 +++++++++++++++++++++ model/common.go | 24 +++ model/config.go | 42 ++++ model/user.go | 52 +++++ pkg/mygin/auth.go | 55 +++++ pkg/mygin/error.go | 37 ++++ pkg/mygin/mygin.go | 36 ++++ resource/static/logo.png | Bin 0 -> 70506 bytes resource/static/main.css | 57 +++++ resource/static/main.js | 79 +++++++ resource/static/semantic-ui-alerts.min.css | 1 + resource/static/semantic-ui-alerts.min.js | 1 + resource/template/common/footer.html | 9 + resource/template/common/header.html | 17 ++ resource/template/common/menu.html | 31 +++ resource/template/component/confirm.html | 12 ++ resource/template/page/error.html | 22 ++ resource/template/page/home.html | 10 + resource/template/page/login.html | 20 ++ service/dao/dao.go | 20 ++ service/{handler => rpc}/auth.go | 2 +- service/{handler => rpc}/nezha.go | 2 +- 33 files changed, 1112 insertions(+), 21 deletions(-) create mode 100644 cmd/dashboard/controller/common_page.go create mode 100644 cmd/dashboard/controller/controller.go create mode 100644 cmd/dashboard/controller/guest_page.go create mode 100644 cmd/dashboard/controller/member_api.go create mode 100644 cmd/dashboard/controller/member_page.go create mode 100644 cmd/dashboard/controller/oauth2.go create mode 100644 cmd/dashboard/rpc/rpc.go create mode 100644 model/common.go create mode 100644 model/config.go create mode 100644 model/user.go create mode 100644 pkg/mygin/auth.go create mode 100644 pkg/mygin/error.go create mode 100644 pkg/mygin/mygin.go create mode 100644 resource/static/logo.png create mode 100644 resource/static/main.css create mode 100644 resource/static/main.js create mode 100644 resource/static/semantic-ui-alerts.min.css create mode 100644 resource/static/semantic-ui-alerts.min.js create mode 100644 resource/template/common/footer.html create mode 100644 resource/template/common/header.html create mode 100644 resource/template/common/menu.html create mode 100644 resource/template/component/confirm.html create mode 100644 resource/template/page/error.html create mode 100644 resource/template/page/home.html create mode 100644 resource/template/page/login.html create mode 100644 service/dao/dao.go rename service/{handler => rpc}/auth.go (98%) rename service/{handler => rpc}/nezha.go (98%) diff --git a/.gitignore b/.gitignore index 308e813..7a577be 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out + +/data +.DS_Store \ No newline at end of file diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 3924dd1..a883252 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -9,12 +9,12 @@ import ( "google.golang.org/grpc" pb "github.com/p14yground/nezha/proto" - "github.com/p14yground/nezha/service/handler" "github.com/p14yground/nezha/service/monitor" + "github.com/p14yground/nezha/service/rpc" ) func main() { - auth := handler.AuthHandler{ + auth := rpc.AuthHandler{ AppKey: "naiba", AppSecret: "123456", } diff --git a/cmd/dashboard/controller/common_page.go b/cmd/dashboard/controller/common_page.go new file mode 100644 index 0000000..767657e --- /dev/null +++ b/cmd/dashboard/controller/common_page.go @@ -0,0 +1,32 @@ +package controller + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/p14yground/nezha/model" + "github.com/p14yground/nezha/pkg/mygin" + "github.com/p14yground/nezha/service/dao" +) + +type commonPage struct { + r *gin.Engine +} + +func (cp *commonPage) serve() { + cr := cp.r.Group("") + cr.Use(mygin.Authorize(mygin.AuthorizeOption{})) + cr.GET("/", cp.home) +} + +func (cp *commonPage) home(c *gin.Context) { + var admin *model.User + isLogin, ok := c.Get(model.CtxKeyIsUserLogin) + if ok && isLogin.(bool) { + admin = dao.Admin + } + c.HTML(http.StatusOK, "page/home", mygin.CommonEnvironment(c, gin.H{ + "Admin": admin, + })) +} diff --git a/cmd/dashboard/controller/controller.go b/cmd/dashboard/controller/controller.go new file mode 100644 index 0000000..7c373da --- /dev/null +++ b/cmd/dashboard/controller/controller.go @@ -0,0 +1,51 @@ +package controller + +import ( + "fmt" + "html/template" + "time" + + "github.com/gin-gonic/gin" + + "github.com/p14yground/nezha/pkg/mygin" + "github.com/p14yground/nezha/service/dao" +) + +// ServeWeb .. +func ServeWeb() { + r := gin.Default() + r.Use(mygin.RecordPath) + r.SetFuncMap(template.FuncMap{ + "tf": func(t time.Time) string { + return t.Format("2006年1月2号") + }, + "fs": func() string { + if !dao.Conf.Debug { + return "" + } + return fmt.Sprintf("%d", time.Now().UnixNano()) + }, + }) + r.Static("/static", "resource/static") + r.LoadHTMLGlob("resource/template/**/*") + routers(r) + r.Run() +} + +func routers(r *gin.Engine) { + // 通用页面 + cp := commonPage{r} + cp.serve() + // 游客页面 + gp := guestPage{r} + gp.serve() + // 会员页面 + mp := &memberPage{r} + mp.serve() + // API + api := r.Group("api") + { + ma := &memberAPI{api} + ma.serve() + } +} diff --git a/cmd/dashboard/controller/guest_page.go b/cmd/dashboard/controller/guest_page.go new file mode 100644 index 0000000..54aa162 --- /dev/null +++ b/cmd/dashboard/controller/guest_page.go @@ -0,0 +1,45 @@ +package controller + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/p14yground/nezha/pkg/mygin" + "github.com/p14yground/nezha/service/dao" + "golang.org/x/oauth2" + "golang.org/x/oauth2/github" +) + +type guestPage struct { + r *gin.Engine +} + +func (gp *guestPage) serve() { + gr := gp.r.Group("") + gr.Use(mygin.Authorize(mygin.AuthorizeOption{ + Guest: true, + IsPage: true, + Msg: "您已登录", + Btn: "返回首页", + Redirect: "/", + })) + + gr.GET("/login", gp.login) + + oauth := &oauth2controller{ + oauth2Config: &oauth2.Config{ + ClientID: dao.Conf.GitHub.ClientID, + ClientSecret: dao.Conf.GitHub.ClientSecret, + Scopes: []string{}, + Endpoint: github.Endpoint, + }, + r: gr, + } + oauth.serve() +} + +func (gp *guestPage) login(c *gin.Context) { + c.HTML(http.StatusOK, "page/login", mygin.CommonEnvironment(c, gin.H{ + "Title": "登录", + })) +} diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go new file mode 100644 index 0000000..c720251 --- /dev/null +++ b/cmd/dashboard/controller/member_api.go @@ -0,0 +1,57 @@ +package controller + +import ( + "fmt" + "net/http" + "time" + + "github.com/gin-gonic/gin" + + "github.com/p14yground/nezha/model" + "github.com/p14yground/nezha/pkg/mygin" + "github.com/p14yground/nezha/service/dao" +) + +type memberAPI struct { + r gin.IRouter +} + +func (ma *memberAPI) serve() { + mr := ma.r.Group("") + mr.Use(mygin.Authorize(mygin.AuthorizeOption{ + Member: true, + IsPage: false, + Msg: "访问此接口需要登录", + Btn: "点此登录", + Redirect: "/login", + })) + + mr.POST("/logout", ma.logout) +} + +type logoutForm struct { + ID uint64 +} + +func (ma *memberAPI) logout(c *gin.Context) { + var lf logoutForm + if err := c.ShouldBindJSON(&lf); err != nil { + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusBadRequest, + Message: fmt.Sprintf("请求错误:%s", err), + }) + return + } + if lf.ID != dao.Admin.ID { + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusBadRequest, + Message: fmt.Sprintf("请求错误:%s", "用户ID不匹配"), + }) + return + } + dao.Admin.Token = "" + dao.Admin.TokenExpired = time.Now() + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusOK, + }) +} diff --git a/cmd/dashboard/controller/member_page.go b/cmd/dashboard/controller/member_page.go new file mode 100644 index 0000000..39061cf --- /dev/null +++ b/cmd/dashboard/controller/member_page.go @@ -0,0 +1,21 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "github.com/p14yground/nezha/pkg/mygin" +) + +type memberPage struct { + r *gin.Engine +} + +func (mp *memberPage) serve() { + mr := mp.r.Group("") + mr.Use(mygin.Authorize(mygin.AuthorizeOption{ + Member: true, + IsPage: true, + Msg: "此页面需要登录", + Btn: "点此登录", + Redirect: "/login", + })) +} diff --git a/cmd/dashboard/controller/oauth2.go b/cmd/dashboard/controller/oauth2.go new file mode 100644 index 0000000..b31a0b2 --- /dev/null +++ b/cmd/dashboard/controller/oauth2.go @@ -0,0 +1,83 @@ +package controller + +import ( + "context" + "fmt" + "net/http" + + "github.com/naiba/com" + + "github.com/gin-gonic/gin" + GitHubAPI "github.com/google/go-github/v28/github" + "golang.org/x/oauth2" + + "github.com/p14yground/nezha/model" + "github.com/p14yground/nezha/pkg/mygin" + "github.com/p14yground/nezha/service/dao" +) + +type oauth2controller struct { + oauth2Config *oauth2.Config + r gin.IRoutes +} + +func (oa *oauth2controller) serve() { + oa.r.GET("/oauth2/login", oa.login) + oa.r.GET("/oauth2/callback", oa.callback) +} + +func (oa *oauth2controller) login(c *gin.Context) { + state := com.RandomString(6) + dao.Cache.Set(fmt.Sprintf("%s%s", model.CtxKeyOauth2State, c.ClientIP()), state, 0) + url := oa.oauth2Config.AuthCodeURL(state, oauth2.AccessTypeOnline) + c.Redirect(http.StatusFound, url) +} + +func (oa *oauth2controller) callback(c *gin.Context) { + // 验证登录跳转时的 State + state, ok := dao.Cache.Get(fmt.Sprintf("%s%s", model.CtxKeyOauth2State, c.ClientIP())) + if !ok || state.(string) != c.Query("state") { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusBadRequest, + Title: "登录失败", + Msg: fmt.Sprintf("错误信息:%s", "非法的登录方式"), + }, true) + return + } + // 拉取验证用户信息 + ctx := context.Background() + otk, err := oa.oauth2Config.Exchange(ctx, c.Query("code")) + if err != nil { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusBadRequest, + Title: "登录失败", + Msg: fmt.Sprintf("错误信息:%s", err), + }, true) + return + } + oc := oa.oauth2Config.Client(ctx, otk) + client := GitHubAPI.NewClient(oc) + gu, _, err := client.Users.Get(ctx, "") + if err != nil { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusBadRequest, + Title: "登录失败", + Msg: fmt.Sprintf("错误信息:%s", err), + }, true) + return + } + if gu.GetLogin() != dao.Conf.GitHub.Admin { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusBadRequest, + Title: "登录失败", + Msg: fmt.Sprintf("错误信息:%s", "该用户不是本站点管理员,无法登录"), + }, true) + return + } + user := model.NewUserFromGitHub(gu) + dao.Admin = &user + dao.Admin.IssueNewToken() + c.SetCookie(dao.Conf.Site.CookieName, dao.Admin.Token, 60*60*24*14, "", "", false, false) + c.Status(http.StatusOK) + c.Writer.WriteString("") +} diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go index ba9acd8..736a26e 100644 --- a/cmd/dashboard/main.go +++ b/cmd/dashboard/main.go @@ -1,27 +1,36 @@ package main import ( - "net" + "time" - "google.golang.org/grpc" + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/sqlite" + "github.com/patrickmn/go-cache" - pb "github.com/p14yground/nezha/proto" - "github.com/p14yground/nezha/service/handler" + "github.com/p14yground/nezha/cmd/dashboard/controller" + "github.com/p14yground/nezha/cmd/dashboard/rpc" + "github.com/p14yground/nezha/model" + "github.com/p14yground/nezha/service/dao" ) -func main() { - server := grpc.NewServer() - pb.RegisterNezhaServiceServer(server, &handler.NezhaHandler{ - Auth: &handler.AuthHandler{ - AppKey: "naiba", - AppSecret: "123456", - }, - }) - - lis, err := net.Listen("tcp", ":5555") +func init() { + var err error + dao.Conf, err = model.ReadInConfig("data/config.yaml") if err != nil { panic(err) } - - server.Serve(lis) + dao.Admin = &model.User{ + Login: dao.Conf.GitHub.Admin, + } + dao.DB, err = gorm.Open("sqlite3", "data/sqlite.db") + if err != nil { + panic(err) + } + dao.Cache = cache.New(5*time.Minute, 10*time.Minute) +} + +func main() { + go controller.ServeWeb() + go rpc.ServeRPC() + select {} } diff --git a/cmd/dashboard/rpc/rpc.go b/cmd/dashboard/rpc/rpc.go new file mode 100644 index 0000000..9e27c53 --- /dev/null +++ b/cmd/dashboard/rpc/rpc.go @@ -0,0 +1,26 @@ +package rpc + +import ( + "net" + + "google.golang.org/grpc" + + pb "github.com/p14yground/nezha/proto" + rpcService "github.com/p14yground/nezha/service/rpc" +) + +// ServeRPC ... +func ServeRPC() { + server := grpc.NewServer() + pb.RegisterNezhaServiceServer(server, &rpcService.NezhaHandler{ + Auth: &rpcService.AuthHandler{ + AppKey: "naiba", + AppSecret: "123456", + }, + }) + listen, err := net.Listen("tcp", ":5555") + if err != nil { + panic(err) + } + server.Serve(listen) +} diff --git a/go.mod b/go.mod index 6eff8f9..7406bb2 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,19 @@ go 1.13 require ( github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect + github.com/fsnotify/fsnotify v1.4.7 + github.com/gin-gonic/gin v1.5.0 github.com/go-ole/go-ole v1.2.4 // indirect github.com/golang/protobuf v1.3.2 + github.com/google/go-github/v28 v28.1.1 + github.com/jinzhu/gorm v1.9.11 + github.com/naiba/com v0.0.0-20191104074000-318339dc72a5 + github.com/naiba/poorsquad v0.0.0-20191204154109-4600d20875f6 + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/shirou/gopsutil v2.19.11+incompatible github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect - github.com/stretchr/testify v1.4.0 // indirect + github.com/spf13/viper v1.6.1 + golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect google.golang.org/grpc v1.25.1 ) diff --git a/go.sum b/go.sum index f72409a..447f165 100644 --- a/go.sum +++ b/go.sum @@ -1,68 +1,299 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc= +github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= +github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE= +github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naiba/avalanche v0.0.0-20191105053910-c34b962c2680 h1:ewXaGaPPlBSW7FJK8Cv28MTZ1aSRg1+y6AqPhvTC8t8= +github.com/naiba/avalanche v0.0.0-20191105053910-c34b962c2680/go.mod h1:JDQv0KccC3nw7xdxtS1ZXVNmXwYcI0q2aPI3jE/gWNs= +github.com/naiba/com v0.0.0-20191104074000-318339dc72a5 h1:NYtQRl/K+QXUTLdamg3nHkdI1qGXhN+4B8ZdGktAaVo= +github.com/naiba/com v0.0.0-20191104074000-318339dc72a5/go.mod h1:gU4KduAcIb9RnoirznFwMcPjeSyYQhvXYL9NWEWUMYI= +github.com/naiba/poorsquad v0.0.0-20191204154109-4600d20875f6 h1:iinFCCfcFCfUTWtV7kh3k0HPdetASzjh89eZuiaCmxE= +github.com/naiba/poorsquad v0.0.0-20191204154109-4600d20875f6/go.mod h1:fhqvd8+zGZb3ML/O7fPhUhkJwwGhPhWbvmdW9jfTU7s= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/shirou/gopsutil v2.19.11+incompatible h1:lJHR0foqAjI4exXqWsU3DbH7bX1xvdhGdnXTIARA9W4= github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= +github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= +github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= +gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/model/common.go b/model/common.go new file mode 100644 index 0000000..4602bfb --- /dev/null +++ b/model/common.go @@ -0,0 +1,24 @@ +package model + +import "time" + +// CtxKeyIsUserLogin .. +const CtxKeyIsUserLogin = "ckiul" + +// CtxKeyOauth2State .. +const CtxKeyOauth2State = "cko2s" + +// Common .. +type Common struct { + ID uint64 `gorm:"primary_key"` + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt *time.Time `sql:"index"` +} + +// Response .. +type Response struct { + Code uint64 `json:"code,omitempty"` + Message string `json:"message,omitempty"` + Result interface{} `json:"result,omitempty"` +} diff --git a/model/config.go b/model/config.go new file mode 100644 index 0000000..97d33e4 --- /dev/null +++ b/model/config.go @@ -0,0 +1,42 @@ +package model + +import ( + "github.com/fsnotify/fsnotify" + "github.com/spf13/viper" +) + +// Config .. +type Config struct { + Debug bool + Site struct { + Brand string // 站点名称 + CookieName string // 浏览器 Cookie 名称 + } + GitHub struct { + Admin string // 管理员登录名 + ClientID string + ClientSecret string + } +} + +// ReadInConfig .. +func ReadInConfig(path string) (*Config, error) { + viper.SetConfigFile(path) + err := viper.ReadInConfig() + if err != nil { + return nil, err + } + var c Config + + err = viper.Unmarshal(&c) + if err != nil { + return nil, err + } + + viper.OnConfigChange(func(in fsnotify.Event) { + viper.Unmarshal(&c) + }) + + go viper.WatchConfig() + return &c, nil +} diff --git a/model/user.go b/model/user.go new file mode 100644 index 0000000..f31a456 --- /dev/null +++ b/model/user.go @@ -0,0 +1,52 @@ +package model + +import ( + "fmt" + "time" + + "github.com/google/go-github/v28/github" + "github.com/naiba/com" +) + +// User ... +type User struct { + Common `json:"common,omitempty"` + Login string `gorm:"UNIQUE_INDEX" json:"login,omitempty"` // 登录名 + AvatarURL string `json:"avatar_url,omitempty"` // 头像地址 + Name string `json:"name,omitempty"` // 昵称 + Blog string `json:"blog,omitempty"` // 网站链接 + Email string `json:"email,omitempty"` // 邮箱 + Hireable bool `json:"hireable,omitempty"` + Bio string `json:"bio,omitempty"` // 个人简介 + + Token string `gorm:"UNIQUE_INDEX" json:"-"` // 认证 Token + TokenExpired time.Time `json:"token_expired,omitempty"` // Token 过期时间 + SuperAdmin bool `json:"super_admin,omitempty"` // 超级管理员 + + TeamsID []uint64 `gorm:"-"` +} + +// NewUserFromGitHub .. +func NewUserFromGitHub(gu *github.User) User { + var u User + u.ID = uint64(gu.GetID()) + u.Login = gu.GetLogin() + u.AvatarURL = gu.GetAvatarURL() + u.Name = gu.GetName() + // 昵称为空的情况 + if u.Name == "" { + u.Name = u.Login + } + u.Blog = gu.GetBlog() + u.Blog = gu.GetBlog() + u.Email = gu.GetEmail() + u.Hireable = gu.GetHireable() + u.Bio = gu.GetBio() + return u +} + +// IssueNewToken ... +func (u *User) IssueNewToken() { + u.Token = com.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login)) + u.TokenExpired = time.Now().AddDate(0, 0, 14) +} diff --git a/pkg/mygin/auth.go b/pkg/mygin/auth.go new file mode 100644 index 0000000..cf13d17 --- /dev/null +++ b/pkg/mygin/auth.go @@ -0,0 +1,55 @@ +package mygin + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" + + "github.com/p14yground/nezha/model" + "github.com/p14yground/nezha/service/dao" +) + +// AuthorizeOption .. +type AuthorizeOption struct { + Guest bool + Member bool + IsPage bool + Msg string + Redirect string + Btn string +} + +// Authorize .. +func Authorize(opt AuthorizeOption) func(*gin.Context) { + return func(c *gin.Context) { + token, err := c.Cookie(dao.Conf.Site.CookieName) + var code uint64 = http.StatusForbidden + if opt.Guest { + code = http.StatusBadRequest + } + commonErr := ErrInfo{ + Title: "访问受限", + Code: code, + Msg: opt.Msg, + Link: opt.Redirect, + Btn: opt.Btn, + } + var isLogin bool + if err == nil { + isLogin = token == dao.Admin.Token && dao.Admin.Token != "" && + dao.Admin.TokenExpired.After(time.Now()) + } + c.Set(model.CtxKeyIsUserLogin, isLogin) + // 已登录且只能游客访问 + if isLogin && opt.Guest { + ShowErrorPage(c, commonErr, opt.IsPage) + return + } + // 未登录且需要登录 + if !isLogin && opt.Member { + ShowErrorPage(c, commonErr, opt.IsPage) + return + } + } +} diff --git a/pkg/mygin/error.go b/pkg/mygin/error.go new file mode 100644 index 0000000..46330d9 --- /dev/null +++ b/pkg/mygin/error.go @@ -0,0 +1,37 @@ +package mygin + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/p14yground/nezha/model" +) + +// ErrInfo .. +type ErrInfo struct { + Code uint64 + Title string + Msg string + Link string + Btn string +} + +// ShowErrorPage .. +func ShowErrorPage(c *gin.Context, i ErrInfo, isPage bool) { + if isPage { + c.HTML(http.StatusOK, "page/error", CommonEnvironment(c, gin.H{ + "Code": i.Code, + "Title": i.Title, + "Msg": i.Msg, + "Link": i.Link, + "Btn": i.Btn, + })) + } else { + c.JSON(http.StatusOK, model.Response{ + Code: i.Code, + Message: i.Msg, + }) + } + c.Abort() +} diff --git a/pkg/mygin/mygin.go b/pkg/mygin/mygin.go new file mode 100644 index 0000000..94dd400 --- /dev/null +++ b/pkg/mygin/mygin.go @@ -0,0 +1,36 @@ +package mygin + +import ( + "fmt" + "strings" + + "github.com/gin-gonic/gin" + + "github.com/p14yground/nezha/model" + "github.com/p14yground/nezha/service/dao" +) + +// CommonEnvironment .. +func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H { + data["MatchedPath"] = c.MustGet("MatchedPath") + // 站点标题 + if t, has := data["Title"]; !has { + data["Title"] = dao.Conf.Site.Brand + } else { + data["Title"] = fmt.Sprintf("%s - %s", t, dao.Conf.Site.Brand) + } + isLogin, ok := c.Get(model.CtxKeyIsUserLogin) + if ok && isLogin.(bool) { + data["Admin"] = dao.Admin + } + return data +} + +// RecordPath .. +func RecordPath(c *gin.Context) { + url := c.Request.URL.String() + for _, p := range c.Params { + url = strings.Replace(url, p.Value, ":"+p.Key, 1) + } + c.Set("MatchedPath", url) +} diff --git a/resource/static/logo.png b/resource/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..18b34e1013805a4e12839a274b4de07298c37a09 GIT binary patch literal 70506 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.grid>.row { + border-bottom: solid gainsboro 2px; + padding-bottom: .6em; + margin-bottom: .6em; +} \ No newline at end of file diff --git a/resource/static/main.js b/resource/static/main.js new file mode 100644 index 0000000..d30138e --- /dev/null +++ b/resource/static/main.js @@ -0,0 +1,79 @@ +$('.ui.checkbox').checkbox(); +$('.ui.dropdown').dropdown(); + +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')) { + return false + } + modal.modal({ + closable: true, + onApprove: function () { + confirmBtn.toggleClass('loading') + callFn(extData) + return false + } + }).modal('show') +} + +function showFormModal(modelSelector, formID, URL, getData) { + $(modelSelector).modal({ + closable: true, + onApprove: function () { + let success = false + const btn = $(modelSelector + ' .positive.button') + const form = $(modelSelector + ' form') + if (btn.hasClass('loading')) { + return success + } + form.children('.message').remove() + btn.toggleClass('loading') + const data = getData ? getData() : $(formID).serializeArray().reduce(function (obj, item) { + obj[item.name] = (item.name.endsWith('_id') || item.name === 'id' || item.name === 'permission') ? parseInt(item.value) : item.value; + return obj; + }, {}); + $.post(URL, JSON.stringify(data)).done(function (resp) { + if (resp.code == 200) { + if (resp.message) { + alert(resp.message) + } + window.location.reload() + } else { + form.append(`
操作失败

` + resp.message + `

`) + } + }).fail(function (err) { + form.append(`
网络错误

` + err.responseText + `

`) + }).always(function () { + btn.toggleClass('loading') + }); + return success + } + }).modal('show') +} + +function logout(id) { + $.post('/api/logout', JSON.stringify({ id: id })).done(function (resp) { + if (resp.code == 200) { + $.suiAlert({ + title: '注销成功', + type: 'success', + time: '3', + position: 'top-center', + }); + window.location.reload() + } else { + $.suiAlert({ + title: '注销失败', + description: resp.code + ':' + resp.message, + type: 'error', + time: '3', + position: 'top-center', + }); + } + }).fail(function (err) { + alert('网络错误:' + err.responseText) + }) +} diff --git a/resource/static/semantic-ui-alerts.min.css b/resource/static/semantic-ui-alerts.min.css new file mode 100644 index 0000000..8a146ff --- /dev/null +++ b/resource/static/semantic-ui-alerts.min.css @@ -0,0 +1 @@ +.ui-alerts{position:fixed;z-index:2060;padding:23px}.ui-alerts.center{top:50%;left:50%;margin-top:-100px;margin-left:-222px}.ui-alerts.top-right{top:20px;right:20px}.ui-alerts.top-center{top:20px;margin-left:-222px;left:50%}.ui-alerts.top-left{top:20px;left:20px}.ui-alerts.bottom-right{bottom:0;right:20px}.ui-alerts.bottom-center{bottom:0;margin-left:-222px;left:50%}.ui-alerts.bottom-left{bottom:0;left:20px}.ui-alerts.ui-alerts>.message>.content>.header{padding-right:13px}@media (min-width:320px){.ui-alerts.top-center{margin-left:-163px}} \ No newline at end of file diff --git a/resource/static/semantic-ui-alerts.min.js b/resource/static/semantic-ui-alerts.min.js new file mode 100644 index 0000000..f6f7a8b --- /dev/null +++ b/resource/static/semantic-ui-alerts.min.js @@ -0,0 +1 @@ +$.suiAlert=function(i){function t(){l=setTimeout(function(){c.transition({animation:e,duration:"2s",onComplete:function(){c.remove()}})},1e3*o.time)}var o=$.extend({title:"Semantic UI Alerts",description:"semantic ui alerts library",type:"error",time:5,position:"top-right",icon:!1},i);o.icon===!1&&("info"==o.type?o.icon="announcement":"success"==o.type?o.icon="checkmark":"error"==o.type?o.icon="remove":"warning"==o.type&&(o.icon="warning circle"));var e="drop";"top-right"==o.position?e="fly left":"top-center"==o.position?e="fly down":"top-left"==o.position?e="fly right":"bottom-right"==o.position?e="fly left":"bottom-center"==o.position?e="fly up":"bottom-left"==o.position&&(e="fly right");var n="",r=$(window).width();r<425&&(n="mini");var s="ui-alerts."+o.position;$("body > ."+s).length||$("body").append('
');var c=$('
'+o.title+"

"+o.description+"

");$("."+s).prepend(c),c.transition("pulse"),$("#alertclose").on("click",function(){$(this).closest("#alert").transition({animation:e,onComplete:function(){c.remove()}})});var l=0;$(c).mouseenter(function(){clearTimeout(l)}).mouseleave(function(){t()}),t()}; \ No newline at end of file diff --git a/resource/template/common/footer.html b/resource/template/common/footer.html new file mode 100644 index 0000000..f30fbe1 --- /dev/null +++ b/resource/template/common/footer.html @@ -0,0 +1,9 @@ +{{define "common/footer"}} + + + + + + + +{{end}} \ No newline at end of file diff --git a/resource/template/common/header.html b/resource/template/common/header.html new file mode 100644 index 0000000..2674067 --- /dev/null +++ b/resource/template/common/header.html @@ -0,0 +1,17 @@ +{{define "common/header"}} + + + + + + + + {{.Title}} + + + + + + + + {{end}} \ No newline at end of file diff --git a/resource/template/common/menu.html b/resource/template/common/menu.html new file mode 100644 index 0000000..0f3ee3b --- /dev/null +++ b/resource/template/common/menu.html @@ -0,0 +1,31 @@ +{{define "common/menu"}} + +{{template "component/confirm" .}} +{{end}} \ No newline at end of file diff --git a/resource/template/component/confirm.html b/resource/template/component/confirm.html new file mode 100644 index 0000000..43ca780 --- /dev/null +++ b/resource/template/component/confirm.html @@ -0,0 +1,12 @@ +{{define "component/confirm"}} + +{{end}} \ No newline at end of file diff --git a/resource/template/page/error.html b/resource/template/page/error.html new file mode 100644 index 0000000..4e78d1e --- /dev/null +++ b/resource/template/page/error.html @@ -0,0 +1,22 @@ +{{define "page/error"}} +{{template "common/header" .}} + +{{template "common/footer" .}} +{{end}} \ No newline at end of file diff --git a/resource/template/page/home.html b/resource/template/page/home.html new file mode 100644 index 0000000..ca80354 --- /dev/null +++ b/resource/template/page/home.html @@ -0,0 +1,10 @@ +{{define "page/home"}} +{{template "common/header" .}} +{{template "common/menu" .}} +
+
+ {{.Admin}} +
+
+{{template "common/footer" .}} +{{end}} \ No newline at end of file diff --git a/resource/template/page/login.html b/resource/template/page/login.html new file mode 100644 index 0000000..6186e46 --- /dev/null +++ b/resource/template/page/login.html @@ -0,0 +1,20 @@ +{{define "page/login"}} +{{template "common/header" .}} + +{{template "common/footer" .}} +{{end}} \ No newline at end of file diff --git a/service/dao/dao.go b/service/dao/dao.go new file mode 100644 index 0000000..9f93586 --- /dev/null +++ b/service/dao/dao.go @@ -0,0 +1,20 @@ +package dao + +import ( + "github.com/jinzhu/gorm" + "github.com/patrickmn/go-cache" + + "github.com/p14yground/nezha/model" +) + +// Conf .. +var Conf *model.Config + +// Cache .. +var Cache *cache.Cache + +// DB .. +var DB *gorm.DB + +// Admin .. +var Admin *model.User diff --git a/service/handler/auth.go b/service/rpc/auth.go similarity index 98% rename from service/handler/auth.go rename to service/rpc/auth.go index 30aa999..aeca03e 100644 --- a/service/handler/auth.go +++ b/service/rpc/auth.go @@ -1,4 +1,4 @@ -package handler +package rpc import ( "context" diff --git a/service/handler/nezha.go b/service/rpc/nezha.go similarity index 98% rename from service/handler/nezha.go rename to service/rpc/nezha.go index e14b157..5a15f63 100644 --- a/service/handler/nezha.go +++ b/service/rpc/nezha.go @@ -1,4 +1,4 @@ -package handler +package rpc import ( "context"