From 77ddb3747b443e523b8cbd1497b36ac7da603bda Mon Sep 17 00:00:00 2001 From: Ivan Reshetnikov Date: Wed, 1 Nov 2023 22:40:15 +0500 Subject: [PATCH] Refactor API handlers --- views/auth.go | 164 ++++++++++++++++++++++++---------------------- views/errors.go | 6 +- views/groups.go | 88 +++++++++++++------------ views/index.go | 30 +++++---- views/links.go | 126 ++++++++++++++++++----------------- views/main.go | 52 ++++----------- views/settings.go | 30 +++++---- 7 files changed, 247 insertions(+), 249 deletions(-) diff --git a/views/auth.go b/views/auth.go index f536f06..8d548e5 100644 --- a/views/auth.go +++ b/views/auth.go @@ -15,22 +15,24 @@ import ( const TOKEN_LIFETIME_IN_SECONDS = 60 * 60 * 24 * 30 -func ShowRegistrationForm(c *gin.Context, db *gorm.DB) { - if database.CountAdmins(db) > 0 { - ShowError(c, errors.New("At least 1 user already exists")) - return - } +func ShowRegistrationForm(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + if database.CountAdmins(db) > 0 { + ShowError(ctx, errors.New("At least 1 user already exists")) + return + } - c.HTML(http.StatusOK, "auth.html.tmpl", gin.H{ - "title": "Create an account", - "description": "To prevent other people from seeing your links, create an account.", - "button": "Create", - "formAction": "/api/users", - }) + ctx.HTML(http.StatusOK, "auth.html.tmpl", gin.H{ + "title": "Create an account", + "description": "To prevent other people from seeing your links, create an account.", + "button": "Create", + "formAction": "/api/users", + }) + } } -func ShowLoginForm(c *gin.Context) { - c.HTML(http.StatusOK, "auth.html.tmpl", gin.H{ +func ShowLoginForm(ctx *gin.Context) { + ctx.HTML(http.StatusOK, "auth.html.tmpl", gin.H{ "title": "Sign in", "description": "Authorization is required to view this page.", "button": "Sign in", @@ -69,37 +71,39 @@ func RequireAuth(c *gin.Context, cfg *config.Config) (*jwt.RegisteredClaims, err return claims, nil } -func AuthMiddleware(c *gin.Context, db *gorm.DB, cfg *config.Config) { - claims, err := RequireAuth(c, cfg) - if err != nil { - if cfg.HeaderAuth && c.Request.Header.Get("Remote-User") != "" { - // Generate access token. - token, err := GetJWTToken(cfg) - if err != nil { - ShowError(c, err) +func AuthMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { + return func(ctx *gin.Context) { + claims, err := RequireAuth(ctx, cfg) + if err != nil { + if cfg.HeaderAuth && ctx.Request.Header.Get("Remote-User") != "" { + // Generate access token. + token, err := GetJWTToken(cfg) + if err != nil { + ShowError(ctx, err) + return + } + SetTokenCookie(ctx, token, cfg) return } - SetTokenCookie(c, token, cfg) + + if database.CountAdmins(db) < 1 { + ctx.Redirect(http.StatusFound, "/registration") + } else { + ctx.Redirect(http.StatusFound, "/signin") + } + ctx.Abort() return } - if database.CountAdmins(db) < 1 { - c.Redirect(http.StatusFound, "/registration") - } else { - c.Redirect(http.StatusFound, "/signin") + // Create a new token if the old one is about to expire + if time.Now().Add(time.Second * (TOKEN_LIFETIME_IN_SECONDS / 2)).After(claims.ExpiresAt.Time) { + newToken, err := GetJWTToken(cfg) + if err != nil { + ShowError(ctx, err) + return + } + SetTokenCookie(ctx, newToken, cfg) } - c.Abort() - return - } - - // Create a new token if the old one is about to expire - if time.Now().Add(time.Second * (TOKEN_LIFETIME_IN_SECONDS / 2)).After(claims.ExpiresAt.Time) { - newToken, err := GetJWTToken(cfg) - if err != nil { - ShowError(c, err) - return - } - SetTokenCookie(c, newToken, cfg) } } @@ -111,53 +115,57 @@ func GetJWTToken(cfg *config.Config) (string, error) { return token.SignedString([]byte(cfg.SecretKey)) } -func CreateUser(c *gin.Context, db *gorm.DB, cfg *config.Config) { - if database.CountAdmins(db) > 0 { - ShowError(c, errors.New("At least 1 user already exists")) - return - } +func CreateUser(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { + return func(ctx *gin.Context) { + if database.CountAdmins(db) > 0 { + ShowError(ctx, errors.New("At least 1 user already exists")) + return + } - // Try to create a user. - username := c.PostForm("username") - password := c.PostForm("password") - _, err := database.CreateAdmin(db, username, password) - if err != nil { - ShowError(c, err) - return - } + // Try to create a user. + username := ctx.PostForm("username") + password := ctx.PostForm("password") + _, err := database.CreateAdmin(db, username, password) + if err != nil { + ShowError(ctx, err) + return + } - // Generate access token. - token, err := GetJWTToken(cfg) - if err != nil { - ShowError(c, err) - return - } - SetTokenCookie(c, token, cfg) + // Generate access token. + token, err := GetJWTToken(cfg) + if err != nil { + ShowError(ctx, err) + return + } + SetTokenCookie(ctx, token, cfg) - // Redirect to homepage. - c.Redirect(http.StatusFound, "/") + // Redirect to homepage. + ctx.Redirect(http.StatusFound, "/") + } } -func AuthorizeUser(c *gin.Context, db *gorm.DB, cfg *config.Config) { - // Check credentials. - username := c.PostForm("username") - password := c.PostForm("password") - _, err := database.AuthorizeAdmin(db, username, password) - if err != nil { - ShowError(c, err) - return - } +func AuthorizeUser(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { + return func(ctx *gin.Context) { + // Check credentials. + username := ctx.PostForm("username") + password := ctx.PostForm("password") + _, err := database.AuthorizeAdmin(db, username, password) + if err != nil { + ShowError(ctx, err) + return + } - // Generate an access token. - token, err := GetJWTToken(cfg) - if err != nil { - ShowError(c, err) - return - } - SetTokenCookie(c, token, cfg) + // Generate an access token. + token, err := GetJWTToken(cfg) + if err != nil { + ShowError(ctx, err) + return + } + SetTokenCookie(ctx, token, cfg) - // Redirect to homepage. - c.Redirect(http.StatusFound, "/") + // Redirect to homepage. + ctx.Redirect(http.StatusFound, "/") + } } // Save token in cookies diff --git a/views/errors.go b/views/errors.go index e2d20a7..ac6d456 100644 --- a/views/errors.go +++ b/views/errors.go @@ -5,13 +5,13 @@ import ( "net/http" ) -func ShowError(c *gin.Context, err error) { - c.HTML( +func ShowError(ctx *gin.Context, err error) { + ctx.HTML( http.StatusBadRequest, "error.html.tmpl", gin.H{ "error": err.Error(), }, ) - c.Abort() + ctx.Abort() } diff --git a/views/groups.go b/views/groups.go index e498f01..ee2896d 100644 --- a/views/groups.go +++ b/views/groups.go @@ -8,55 +8,61 @@ import ( "strconv" ) -func CreateGroup(c *gin.Context, db *gorm.DB) { - // Save new group to the database. - group := database.Group{ - Name: c.PostForm("groupName"), - } - if result := db.Create(&group); result.Error != nil { - ShowError(c, result.Error) - return - } +func CreateGroup(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + // Save new group to the database. + group := database.Group{ + Name: ctx.PostForm("groupName"), + } + if result := db.Create(&group); result.Error != nil { + ShowError(ctx, result.Error) + return + } - // This page is called from the settings, return the user back. - c.Redirect(http.StatusFound, "/settings") + // This page is called from the settings, return the user back. + ctx.Redirect(http.StatusFound, "/settings") + } } -func UpdateGroup(c *gin.Context, db *gorm.DB) { - id, err := strconv.ParseUint(c.Param("id"), 10, 64) - if err != nil { - ShowError(c, err) - return - } +func UpdateGroup(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + id, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + if err != nil { + ShowError(ctx, err) + return + } - var group database.Group - if result := db.First(&group, id); result.Error != nil { - ShowError(c, result.Error) - return - } + var group database.Group + if result := db.First(&group, id); result.Error != nil { + ShowError(ctx, result.Error) + return + } - group.Name = c.PostForm("groupName") - if result := db.Save(&group); result.Error != nil { - ShowError(c, result.Error) - return - } + group.Name = ctx.PostForm("groupName") + if result := db.Save(&group); result.Error != nil { + ShowError(ctx, result.Error) + return + } - // This page is called from the settings, return the user back. - c.Redirect(http.StatusFound, "/settings") + // This page is called from the settings, return the user back. + ctx.Redirect(http.StatusFound, "/settings") + } } -func DeleteGroup(c *gin.Context, db *gorm.DB) { - id, err := strconv.ParseUint(c.Param("id"), 10, 64) - if err != nil { - ShowError(c, err) - return - } +func DeleteGroup(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + id, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + if err != nil { + ShowError(ctx, err) + return + } - if result := db.Delete(&database.Group{}, id); result.Error != nil { - ShowError(c, result.Error) - return - } + if result := db.Delete(&database.Group{}, id); result.Error != nil { + ShowError(ctx, result.Error) + return + } - // Redirect to settings. - c.Redirect(http.StatusFound, "/settings") + // Redirect to settings. + ctx.Redirect(http.StatusFound, "/settings") + } } diff --git a/views/index.go b/views/index.go index 52d63bf..c0ad2be 100644 --- a/views/index.go +++ b/views/index.go @@ -7,20 +7,22 @@ import ( "net/http" ) -func ShowMainPage(c *gin.Context, db *gorm.DB) { - // Get a list of groups with links - var groups []database.Group - result := db. - Model(&database.Group{}). - Preload("Links"). - Find(&groups) +func ShowMainPage(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + // Get a list of groups with links + var groups []database.Group + result := db. + Model(&database.Group{}). + Preload("Links"). + Find(&groups) - if result.Error != nil { - ShowError(c, result.Error) - return + if result.Error != nil { + ShowError(ctx, result.Error) + return + } + + ctx.HTML(http.StatusOK, "index.html.tmpl", gin.H{ + "groups": groups, + }) } - - c.HTML(http.StatusOK, "index.html.tmpl", gin.H{ - "groups": groups, - }) } diff --git a/views/links.go b/views/links.go index 43561ef..8b7fd68 100644 --- a/views/links.go +++ b/views/links.go @@ -8,75 +8,81 @@ import ( "strconv" ) -func CreateLink(c *gin.Context, db *gorm.DB) { - groupID, err := strconv.ParseUint(c.PostForm("groupID"), 10, 32) - if err != nil { - ShowError(c, err) - return - } +func CreateLink(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + groupID, err := strconv.ParseUint(ctx.PostForm("groupID"), 10, 32) + if err != nil { + ShowError(ctx, err) + return + } - link := database.Link{ - Name: c.PostForm("linkName"), - Href: c.PostForm("href"), - GroupID: groupID, - } - icon := c.PostForm("icon") - if icon == "" { - link.Icon = nil - } else { - link.Icon = &icon - } - if result := db.Create(&link); result.Error != nil { - ShowError(c, result.Error) - return - } + link := database.Link{ + Name: ctx.PostForm("linkName"), + Href: ctx.PostForm("href"), + GroupID: groupID, + } + icon := ctx.PostForm("icon") + if icon == "" { + link.Icon = nil + } else { + link.Icon = &icon + } + if result := db.Create(&link); result.Error != nil { + ShowError(ctx, result.Error) + return + } - // Redirect to settings. - c.Redirect(http.StatusFound, "/settings") + // Redirect to settings. + ctx.Redirect(http.StatusFound, "/settings") + } } -func UpdateLink(c *gin.Context, db *gorm.DB) { - id, err := strconv.ParseUint(c.Param("id"), 10, 64) - if err != nil { - ShowError(c, err) - return - } +func UpdateLink(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + id, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + if err != nil { + ShowError(ctx, err) + return + } - var link database.Link - if result := db.First(&link, id); result.Error != nil { - ShowError(c, err) - return - } + var link database.Link + if result := db.First(&link, id); result.Error != nil { + ShowError(ctx, err) + return + } - link.Name = c.PostForm("linkName") - link.Href = c.PostForm("href") - icon := c.PostForm("icon") - if icon == "" { - link.Icon = nil - } else { - link.Icon = &icon - } - if result := db.Save(&link); result.Error != nil { - ShowError(c, result.Error) - return - } + link.Name = ctx.PostForm("linkName") + link.Href = ctx.PostForm("href") + icon := ctx.PostForm("icon") + if icon == "" { + link.Icon = nil + } else { + link.Icon = &icon + } + if result := db.Save(&link); result.Error != nil { + ShowError(ctx, result.Error) + return + } - // Redirect to settings. - c.Redirect(http.StatusFound, "/settings") + // Redirect to settings. + ctx.Redirect(http.StatusFound, "/settings") + } } -func DeleteLink(c *gin.Context, db *gorm.DB) { - id, err := strconv.ParseUint(c.Param("id"), 10, 64) - if err != nil { - ShowError(c, err) - return - } +func DeleteLink(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + id, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + if err != nil { + ShowError(ctx, err) + return + } - if result := db.Delete(&database.Link{}, id); result.Error != nil { - ShowError(c, result.Error) - return - } + if result := db.Delete(&database.Link{}, id); result.Error != nil { + ShowError(ctx, result.Error) + return + } - // Redirect to settings. - c.Redirect(http.StatusFound, "/settings") + // Redirect to settings. + ctx.Redirect(http.StatusFound, "/settings") + } } diff --git a/views/main.go b/views/main.go index 25289cb..327f7db 100644 --- a/views/main.go +++ b/views/main.go @@ -22,68 +22,42 @@ func GetGinEngine(cfg *config.Config, db *gorm.DB) *gin.Engine { engine.Use(SecurityHeadersMiddleware) - engine.GET("/signin", func(c *gin.Context) { - ShowLoginForm(c) - }) - engine.POST("/api/users/signin", func(c *gin.Context) { - AuthorizeUser(c, db, cfg) - }) + engine.GET("/signin", ShowLoginForm) + engine.POST("/api/users/signin", AuthorizeUser(db, cfg)) - engine.GET("/registration", func(c *gin.Context) { - ShowRegistrationForm(c, db) - }) - engine.POST("/api/users", func(c *gin.Context) { - CreateUser(c, db, cfg) - }) + engine.GET("/registration", ShowRegistrationForm(db)) + engine.POST("/api/users", CreateUser(db, cfg)) // This group requires authorization before viewing. protected := engine.Group("/") - protected.Use(func(c *gin.Context) { - AuthMiddleware(c, db, cfg) - }) + protected.Use(AuthMiddleware(db, cfg)) // Main page - protected.GET("/", func(c *gin.Context) { - ShowMainPage(c, db) - }) + protected.GET("/", ShowMainPage(db)) - protected.GET("/settings", func(c *gin.Context) { - ShowSettings(c, db) - }) + protected.GET("/settings", ShowSettings(db)) // Create new group - protected.POST("/api/groups", func(c *gin.Context) { - CreateGroup(c, db) - }) + protected.POST("/api/groups", CreateGroup(db)) // Update group // HTML forms cannot be submitted using PUT or PATCH methods without javascript. - protected.POST("/api/groups/:id/put", func(c *gin.Context) { - UpdateGroup(c, db) - }) + protected.POST("/api/groups/:id/put", UpdateGroup(db)) // Delete group // HTML forms cannot be submitted using the DELETE method without javascript. - protected.POST("/api/groups/:id/delete", func(c *gin.Context) { - DeleteGroup(c, db) - }) + protected.POST("/api/groups/:id/delete", DeleteGroup(db)) // Create new link - protected.POST("/api/links", func(c *gin.Context) { - CreateLink(c, db) - }) + protected.POST("/api/links", CreateLink(db)) // Update link. // HTML forms cannot be submitted using PUT or PATCH methods without javascript. - protected.POST("/api/links/:id/put", func(c *gin.Context) { - UpdateLink(c, db) - }) + protected.POST("/api/links/:id/put", UpdateLink(db)) // Delete link // HTML forms cannot be submitted using the DELETE method without javascript. - protected.POST("/api/links/:id/delete", func(c *gin.Context) { - DeleteLink(c, db) - }) + protected.POST("/api/links/:id/delete", DeleteLink(db)) return engine } diff --git a/views/settings.go b/views/settings.go index 8c65e59..8379561 100644 --- a/views/settings.go +++ b/views/settings.go @@ -7,20 +7,22 @@ import ( "net/http" ) -func ShowSettings(c *gin.Context, db *gorm.DB) { - // Get a list of groups with links - var groups []database.Group - result := db. - Model(&database.Group{}). - Preload("Links"). - Find(&groups) +func ShowSettings(db *gorm.DB) gin.HandlerFunc { + return func(ctx *gin.Context) { + // Get a list of groups with links + var groups []database.Group + result := db. + Model(&database.Group{}). + Preload("Links"). + Find(&groups) - if result.Error != nil { - ShowError(c, result.Error) - return + if result.Error != nil { + ShowError(ctx, result.Error) + return + } + + ctx.HTML(http.StatusOK, "settings.html.tmpl", gin.H{ + "groups": groups, + }) } - - c.HTML(http.StatusOK, "settings.html.tmpl", gin.H{ - "groups": groups, - }) }