From 6af94245b638d1b767271fbd2b3eb6150c93bc3c Mon Sep 17 00:00:00 2001 From: Geno Date: Mon, 19 Jul 2021 17:59:08 +0200 Subject: [PATCH] web: move global module register to service --- web/api/status/main.go | 26 +++++----- web/api/status/main_test.go | 2 +- web/auth/api_login.go | 76 +++++++++++++++--------------- web/auth/api_login_test.go | 2 +- web/auth/api_my_delete.go | 30 ++++++------ web/auth/api_my_delete_test.go | 2 +- web/auth/api_my_password.go | 60 ++++++++++++----------- web/auth/api_my_password_test.go | 2 +- web/auth/api_my_status.go | 14 +++--- web/auth/api_my_status_test.go | 4 +- web/auth/api_password_code.go | 74 ++++++++++++++--------------- web/auth/api_password_code_test.go | 2 +- web/auth/main.go | 15 ++++++ web/main.go | 4 ++ web/metrics/main.go | 68 +++++++++++++------------- web/metrics/main_test.go | 2 +- web/module.go | 12 ++--- web/webtest/main.go | 24 ++++++---- 18 files changed, 214 insertions(+), 205 deletions(-) create mode 100644 web/auth/main.go diff --git a/web/api/status/main.go b/web/api/status/main.go index b28670a..5e18b1e 100644 --- a/web/api/status/main.go +++ b/web/api/status/main.go @@ -34,19 +34,17 @@ type Status struct { // @Failure 400 {object} web.HTTPError // @Failure 404 {object} web.HTTPError // @Router /api/status [get] -func init() { - web.ModuleRegister(func(r *gin.Engine, ws *web.Service) { - r.GET("/api/status", func(c *gin.Context) { - status := &Status{ - Version: VERSION, - Up: UP(), - Extras: EXTRAS, - } - if !status.Up { - c.JSON(http.StatusInternalServerError, status) - return - } - c.JSON(http.StatusOK, status) - }) +func Register(r *gin.Engine, ws *web.Service) { + r.GET("/api/status", func(c *gin.Context) { + status := &Status{ + Version: VERSION, + Up: UP(), + Extras: EXTRAS, + } + if !status.Up { + c.JSON(http.StatusInternalServerError, status) + return + } + c.JSON(http.StatusOK, status) }) } diff --git a/web/api/status/main_test.go b/web/api/status/main_test.go index f233a1b..2697edf 100644 --- a/web/api/status/main_test.go +++ b/web/api/status/main_test.go @@ -11,7 +11,7 @@ import ( func TestAPIStatus(t *testing.T) { assert := assert.New(t) - s, err := webtest.New() + s, err := webtest.New(Register) assert.NoError(err) defer s.Close() assert.NotNil(s) diff --git a/web/auth/api_login.go b/web/auth/api_login.go index 85ab3ff..89035f5 100644 --- a/web/auth/api_login.go +++ b/web/auth/api_login.go @@ -27,51 +27,49 @@ type login struct { // @Failure 500 {object} web.HTTPError // @Router /api/v1/auth/login [post] // @Param body body login false "login" -func init() { - web.ModuleRegister(func(r *gin.Engine, ws *web.Service) { - r.POST("/api/v1/auth/login", func(c *gin.Context) { - var data login - if err := c.BindJSON(&data); err != nil { - c.JSON(http.StatusBadRequest, web.HTTPError{ - Message: web.APIErrorInvalidRequestFormat, - Error: err.Error(), - }) - return - } +func apiLogin(r *gin.Engine, ws *web.Service) { + r.POST("/api/v1/auth/login", func(c *gin.Context) { + var data login + if err := c.BindJSON(&data); err != nil { + c.JSON(http.StatusBadRequest, web.HTTPError{ + Message: web.APIErrorInvalidRequestFormat, + Error: err.Error(), + }) + return + } - d := &User{} - if err := ws.DB.Where(map[string]interface{}{"username": data.Username}).First(d).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - c.JSON(http.StatusUnauthorized, web.HTTPError{ - Message: APIErrorUserNotFound, - Error: err.Error(), - }) - return - } - c.JSON(http.StatusInternalServerError, web.HTTPError{ - Message: web.APIErrorInternalDatabase, - Error: err.Error(), - }) - return - } - if !d.ValidatePassword(data.Password) { + d := &User{} + if err := ws.DB.Where(map[string]interface{}{"username": data.Username}).First(d).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusUnauthorized, web.HTTPError{ - Message: APIErrorIncorrectPassword, - }) - return - } - - session := sessions.Default(c) - session.Set("user_id", d.ID.String()) - if err := session.Save(); err != nil { - c.JSON(http.StatusBadRequest, web.HTTPError{ - Message: APIErrorCreateSession, + Message: APIErrorUserNotFound, Error: err.Error(), }) return } + c.JSON(http.StatusInternalServerError, web.HTTPError{ + Message: web.APIErrorInternalDatabase, + Error: err.Error(), + }) + return + } + if !d.ValidatePassword(data.Password) { + c.JSON(http.StatusUnauthorized, web.HTTPError{ + Message: APIErrorIncorrectPassword, + }) + return + } - c.JSON(http.StatusOK, d) - }) + session := sessions.Default(c) + session.Set("user_id", d.ID.String()) + if err := session.Save(); err != nil { + c.JSON(http.StatusBadRequest, web.HTTPError{ + Message: APIErrorCreateSession, + Error: err.Error(), + }) + return + } + + c.JSON(http.StatusOK, d) }) } diff --git a/web/auth/api_login_test.go b/web/auth/api_login_test.go index 8842049..494ad37 100644 --- a/web/auth/api_login_test.go +++ b/web/auth/api_login_test.go @@ -12,7 +12,7 @@ import ( func TestAPILogin(t *testing.T) { assert := assert.New(t) - s, err := webtest.NewWithDBSetup(SetupMigration) + s, err := webtest.NewWithDBSetup(apiLogin, SetupMigration) assert.NoError(err) defer s.Close() assert.NotNil(s) diff --git a/web/auth/api_my_delete.go b/web/auth/api_my_delete.go index 1a00c59..15b92eb 100644 --- a/web/auth/api_my_delete.go +++ b/web/auth/api_my_delete.go @@ -17,21 +17,19 @@ import ( // @Failure 500 {object} web.HTTPError // @Router /api/v1/my/profil [delete] // @Security ApiKeyAuth -func init() { - web.ModuleRegister(func(r *gin.Engine, ws *web.Service) { - r.DELETE("/api/v1/my/profil", func(c *gin.Context) { - id, ok := GetCurrentUserID(c) - if !ok { - return - } - if err := ws.DB.Delete(&User{ID: id}).Error; err != nil { - c.JSON(http.StatusInternalServerError, web.HTTPError{ - Message: web.APIErrorInternalDatabase, - Error: err.Error(), - }) - return - } - c.JSON(http.StatusOK, true) - }) +func apiMyDelete(r *gin.Engine, ws *web.Service) { + r.DELETE("/api/v1/my/profil", func(c *gin.Context) { + id, ok := GetCurrentUserID(c) + if !ok { + return + } + if err := ws.DB.Delete(&User{ID: id}).Error; err != nil { + c.JSON(http.StatusInternalServerError, web.HTTPError{ + Message: web.APIErrorInternalDatabase, + Error: err.Error(), + }) + return + } + c.JSON(http.StatusOK, true) }) } diff --git a/web/auth/api_my_delete_test.go b/web/auth/api_my_delete_test.go index 8f0a45e..de3c785 100644 --- a/web/auth/api_my_delete_test.go +++ b/web/auth/api_my_delete_test.go @@ -12,7 +12,7 @@ import ( func TestAPIDeleteMyProfil(t *testing.T) { assert := assert.New(t) - s, err := webtest.NewWithDBSetup(SetupMigration) + s, err := webtest.NewWithDBSetup(Register, SetupMigration) assert.NoError(err) defer s.Close() assert.NotNil(s) diff --git a/web/auth/api_my_password.go b/web/auth/api_my_password.go index b548857..345d2a3 100644 --- a/web/auth/api_my_password.go +++ b/web/auth/api_my_password.go @@ -20,38 +20,36 @@ import ( // @Router /api/v1/my/auth/password [post] // @Security ApiKeyAuth // @Param body body string false "new password" -func init() { - web.ModuleRegister(func(r *gin.Engine, ws *web.Service) { - r.POST("/api/v1/my/auth/password", MiddlewareLogin(ws), func(c *gin.Context) { - d, ok := GetCurrentUser(c, ws) - if !ok { - return - } - var password string - if err := c.BindJSON(&password); err != nil { - c.JSON(http.StatusBadRequest, web.HTTPError{ - Message: web.APIErrorInvalidRequestFormat, - Error: err.Error(), - }) - return - } - if err := d.SetPassword(password); err != nil { - c.JSON(http.StatusInternalServerError, web.HTTPError{ - Message: APIErrroCreatePassword, - Error: err.Error(), - }) - return - } +func apiMyPassword(r *gin.Engine, ws *web.Service) { + r.POST("/api/v1/my/auth/password", MiddlewareLogin(ws), func(c *gin.Context) { + d, ok := GetCurrentUser(c, ws) + if !ok { + return + } + var password string + if err := c.BindJSON(&password); err != nil { + c.JSON(http.StatusBadRequest, web.HTTPError{ + Message: web.APIErrorInvalidRequestFormat, + Error: err.Error(), + }) + return + } + if err := d.SetPassword(password); err != nil { + c.JSON(http.StatusInternalServerError, web.HTTPError{ + Message: APIErrroCreatePassword, + Error: err.Error(), + }) + return + } - if err := ws.DB.Save(&d).Error; err != nil { - c.JSON(http.StatusInternalServerError, web.HTTPError{ - Message: web.APIErrorInternalDatabase, - Error: err.Error(), - }) - return - } + if err := ws.DB.Save(&d).Error; err != nil { + c.JSON(http.StatusInternalServerError, web.HTTPError{ + Message: web.APIErrorInternalDatabase, + Error: err.Error(), + }) + return + } - c.JSON(http.StatusOK, true) - }) + c.JSON(http.StatusOK, true) }) } diff --git a/web/auth/api_my_password_test.go b/web/auth/api_my_password_test.go index c5ac129..33635fb 100644 --- a/web/auth/api_my_password_test.go +++ b/web/auth/api_my_password_test.go @@ -12,7 +12,7 @@ import ( func TestAPIPassword(t *testing.T) { assert := assert.New(t) - s, err := webtest.NewWithDBSetup(SetupMigration) + s, err := webtest.NewWithDBSetup(Register, SetupMigration) assert.NoError(err) defer s.Close() assert.NotNil(s) diff --git a/web/auth/api_my_status.go b/web/auth/api_my_status.go index d8cd539..67b5fd9 100644 --- a/web/auth/api_my_status.go +++ b/web/auth/api_my_status.go @@ -18,13 +18,11 @@ import ( // @Failure 500 {object} web.HTTPError // @Router /api/v1/my/auth/status [get] // @Security ApiKeyAuth -func init() { - web.ModuleRegister(func(r *gin.Engine, ws *web.Service) { - r.GET("/api/v1/my/auth/status", MiddlewareLogin(ws), func(c *gin.Context) { - d, ok := GetCurrentUser(c, ws) - if ok { - c.JSON(http.StatusOK, d) - } - }) +func apiMyStatus(r *gin.Engine, ws *web.Service) { + r.GET("/api/v1/my/auth/status", MiddlewareLogin(ws), func(c *gin.Context) { + d, ok := GetCurrentUser(c, ws) + if ok { + c.JSON(http.StatusOK, d) + } }) } diff --git a/web/auth/api_my_status_test.go b/web/auth/api_my_status_test.go index 728fe6a..da969c0 100644 --- a/web/auth/api_my_status_test.go +++ b/web/auth/api_my_status_test.go @@ -10,9 +10,9 @@ import ( "dev.sum7.eu/genofire/golang-lib/web/webtest" ) -func TestAPIStatus(t *testing.T) { +func TestAPIMyStatus(t *testing.T) { assert := assert.New(t) - s, err := webtest.NewWithDBSetup(SetupMigration) + s, err := webtest.NewWithDBSetup(Register, SetupMigration) assert.NoError(err) defer s.Close() assert.NotNil(s) diff --git a/web/auth/api_password_code.go b/web/auth/api_password_code.go index e3d6ca1..8157354 100644 --- a/web/auth/api_password_code.go +++ b/web/auth/api_password_code.go @@ -28,49 +28,47 @@ type PasswordWithForgetCode struct { // @Failure 500 {object} web.HTTPError // @Router /api/v1/auth/password/code [post] // @Param body body PasswordWithForgetCode false "new password and forget code" -func init() { - web.ModuleRegister(func(r *gin.Engine, ws *web.Service) { - r.POST("/api/v1/auth/password/code", func(c *gin.Context) { - var req PasswordWithForgetCode - if err := c.BindJSON(&req); err != nil { +func apiPasswordCode(r *gin.Engine, ws *web.Service) { + r.POST("/api/v1/auth/password/code", func(c *gin.Context) { + var req PasswordWithForgetCode + if err := c.BindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, web.HTTPError{ + Message: web.APIErrorInvalidRequestFormat, + Error: err.Error(), + }) + return + } + d := User{} + if err := ws.DB.Where("forget_code", req.ForgetCode).First(&d).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusBadRequest, web.HTTPError{ - Message: web.APIErrorInvalidRequestFormat, + Message: APIErrorUserNotFound, Error: err.Error(), }) return } - d := User{} - if err := ws.DB.Where("forget_code", req.ForgetCode).First(&d).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - c.JSON(http.StatusBadRequest, web.HTTPError{ - Message: APIErrorUserNotFound, - Error: err.Error(), - }) - return - } - c.JSON(http.StatusInternalServerError, web.HTTPError{ - Message: APIErrroCreatePassword, - Error: err.Error(), - }) - return - } - if err := d.SetPassword(req.Password); err != nil { - c.JSON(http.StatusInternalServerError, web.HTTPError{ - Message: APIErrroCreatePassword, - Error: err.Error(), - }) - return - } - d.ForgetCode = nil + c.JSON(http.StatusInternalServerError, web.HTTPError{ + Message: APIErrroCreatePassword, + Error: err.Error(), + }) + return + } + if err := d.SetPassword(req.Password); err != nil { + c.JSON(http.StatusInternalServerError, web.HTTPError{ + Message: APIErrroCreatePassword, + Error: err.Error(), + }) + return + } + d.ForgetCode = nil - if err := ws.DB.Save(&d).Error; err != nil { - c.JSON(http.StatusInternalServerError, web.HTTPError{ - Message: web.APIErrorInternalDatabase, - Error: err.Error(), - }) - return - } - c.JSON(http.StatusOK, d.Username) - }) + if err := ws.DB.Save(&d).Error; err != nil { + c.JSON(http.StatusInternalServerError, web.HTTPError{ + Message: web.APIErrorInternalDatabase, + Error: err.Error(), + }) + return + } + c.JSON(http.StatusOK, d.Username) }) } diff --git a/web/auth/api_password_code_test.go b/web/auth/api_password_code_test.go index ec3de91..ec0c0a9 100644 --- a/web/auth/api_password_code_test.go +++ b/web/auth/api_password_code_test.go @@ -13,7 +13,7 @@ import ( func TestAPIPasswordCode(t *testing.T) { assert := assert.New(t) - s, err := webtest.NewWithDBSetup(SetupMigration) + s, err := webtest.NewWithDBSetup(apiPasswordCode, SetupMigration) assert.NoError(err) defer s.Close() assert.NotNil(s) diff --git a/web/auth/main.go b/web/auth/main.go new file mode 100644 index 0000000..f4b6a32 --- /dev/null +++ b/web/auth/main.go @@ -0,0 +1,15 @@ +package auth + +import ( + "dev.sum7.eu/genofire/golang-lib/web" + "github.com/gin-gonic/gin" +) + +// Register to WebService +func Register(r *gin.Engine, ws *web.Service) { + apiLogin(r, ws) + apiMyDelete(r, ws) + apiMyPassword(r, ws) + apiMyStatus(r, ws) + apiPasswordCode(r, ws) +} diff --git a/web/main.go b/web/main.go index 3131730..d3ae4d8 100644 --- a/web/main.go +++ b/web/main.go @@ -3,9 +3,11 @@ package web import ( "github.com/bdlm/log" "github.com/gin-gonic/gin" + // acme "github.com/gin-gonic/autotls" "golang.org/x/crypto/acme/autocert" + // internal "dev.sum7.eu/genofire/golang-lib/mailer" "gorm.io/gorm" @@ -30,6 +32,8 @@ type Service struct { // internal DB *gorm.DB `toml:"-"` Mailer *mailer.Service `toml:"-"` + + modules []ModuleRegisterFunc } // Run to startup all related web parts diff --git a/web/metrics/main.go b/web/metrics/main.go index 9fc9f34..0cbcbc3 100644 --- a/web/metrics/main.go +++ b/web/metrics/main.go @@ -10,6 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + // db metrics gormPrometheus "gorm.io/plugin/prometheus" @@ -27,41 +28,40 @@ var ( } ) -func init() { - web.ModuleRegister(func(r *gin.Engine, ws *web.Service) { - r.Use(ginprom.PromMiddleware(&ginprom.PromOpts{ - EndpointLabelMappingFn: func(c *gin.Context) string { - url := c.Request.URL.Path - for _, p := range c.Params { - url = strings.Replace(url, p.Value, ":"+p.Key, 1) - } - return url - }, +// Register to WebService +func Register(r *gin.Engine, ws *web.Service) { + r.Use(ginprom.PromMiddleware(&ginprom.PromOpts{ + EndpointLabelMappingFn: func(c *gin.Context) string { + url := c.Request.URL.Path + for _, p := range c.Params { + url = strings.Replace(url, p.Value, ":"+p.Key, 1) + } + return url + }, + })) + + prometheus.MustRegister(prometheus.NewGaugeFunc( + prometheus.GaugeOpts{ + Namespace: NAMESPACE, + Name: "up", + Help: "is current version of service running", + ConstLabels: prometheus.Labels{"version": VERSION}, + }, + func() float64 { + if UP() { + return 1 + } + return 0 + }, + )) + + if ws.DB != nil { + ws.DB.Use(gormPrometheus.New(gormPrometheus.Config{ + DBName: NAMESPACE, + RefreshInterval: 15, })) - prometheus.MustRegister(prometheus.NewGaugeFunc( - prometheus.GaugeOpts{ - Namespace: NAMESPACE, - Name: "up", - Help: "is current version of service running", - ConstLabels: prometheus.Labels{"version": VERSION}, - }, - func() float64 { - if UP() { - return 1 - } - return 0 - }, - )) + } - if ws.DB != nil { - ws.DB.Use(gormPrometheus.New(gormPrometheus.Config{ - DBName: NAMESPACE, - RefreshInterval: 15, - })) - - } - - r.GET("/metrics", ginprom.PromHandler(promhttp.Handler())) - }) + r.GET("/metrics", ginprom.PromHandler(promhttp.Handler())) } diff --git a/web/metrics/main_test.go b/web/metrics/main_test.go index 147d17d..6390422 100644 --- a/web/metrics/main_test.go +++ b/web/metrics/main_test.go @@ -11,7 +11,7 @@ import ( func TestMetricsLoaded(t *testing.T) { assert := assert.New(t) - s, err := webtest.New() + s, err := webtest.New(Register) assert.NoError(err) defer s.Close() assert.NotNil(s) diff --git a/web/module.go b/web/module.go index 2d4e149..e73e8db 100644 --- a/web/module.go +++ b/web/module.go @@ -6,24 +6,20 @@ import ( "github.com/gin-gonic/gin" ) -var ( - modules []ModuleRegisterFunc -) - // ModuleRegisterFunc format of module which registered to WebService type ModuleRegisterFunc func(*gin.Engine, *Service) // ModuleRegister used on start of WebService -func ModuleRegister(f ModuleRegisterFunc) { - modules = append(modules, f) +func (ws *Service) ModuleRegister(f ModuleRegisterFunc) { + ws.modules = append(ws.modules, f) } // Bind WebService to gin.Engine func (ws *Service) Bind(r *gin.Engine) { - for _, f := range modules { + for _, f := range ws.modules { f(r, ws) } - log.Infof("loaded %d modules", len(modules)) + log.Infof("loaded %d modules", len(ws.modules)) r.Use(static.Serve("/", static.LocalFile(ws.Webroot, false))) } diff --git a/web/webtest/main.go b/web/webtest/main.go index 8ac1478..d9ebd95 100644 --- a/web/webtest/main.go +++ b/web/webtest/main.go @@ -23,8 +23,9 @@ var ( // Option to configure TestServer type Option struct { - ReRun bool - DBSetup func(db *database.Database) + ReRun bool + DBSetup func(db *database.Database) + ModuleLoader web.ModuleRegisterFunc } type testServer struct { @@ -32,7 +33,7 @@ type testServer struct { Mails chan *mailer.TestingMail Close func() gin *gin.Engine - ws *web.Service + WS *web.Service lastCookies []*http.Cookie } @@ -43,13 +44,17 @@ type Login struct { } // New starts WebService for testing -func New() (*testServer, error) { - return NewWithOption(Option{}) +func New(modules web.ModuleRegisterFunc) (*testServer, error) { + return NewWithOption(Option{ModuleLoader: modules}) } // NewWithDBSetup allows to reconfigure before ReRun the database - e.g. for adding Migration-Steps -func NewWithDBSetup(dbCall func(db *database.Database)) (*testServer, error) { - return NewWithOption(Option{ReRun: true, DBSetup: dbCall}) +func NewWithDBSetup(modules web.ModuleRegisterFunc, dbCall func(db *database.Database)) (*testServer, error) { + return NewWithOption(Option{ + ReRun: true, + DBSetup: dbCall, + ModuleLoader: modules, + }) } // NewWithOption allows to configure WebService for testing @@ -92,6 +97,7 @@ func NewWithOption(option Option) (*testServer, error) { DB: dbConfig.DB, Mailer: mail, } + ws.ModuleRegister(option.ModuleLoader) ws.Session.Name = "mysession" ws.Session.Secret = "hidden" @@ -103,13 +109,13 @@ func NewWithOption(option Option) (*testServer, error) { Mails: mock.Mails, Close: mock.Close, gin: r, - ws: ws, + WS: ws, }, nil } // DatabaseForget, to run a test without a database func (s *testServer) DatabaseForget() { - s.ws.DB = nil + s.WS.DB = nil s.DB = nil }