From 00dcd04eb74e4fd13ea148e41a538b9202e4a07a Mon Sep 17 00:00:00 2001 From: Ivan Reshetnikov Date: Wed, 1 Nov 2023 23:18:33 +0500 Subject: [PATCH] Add the ability to change the title --- config/main.go | 2 ++ readme.md | 5 +++++ templates/head.html.tmpl | 2 +- templates/index.html.tmpl | 2 +- views/auth.go | 36 +++++++++++++++++++----------------- views/errors.go | 13 +++++-------- views/groups.go | 19 ++++++++++--------- views/index.go | 7 ++++--- views/links.go | 21 +++++++++++---------- views/main.go | 20 ++++++++++---------- views/render.go | 12 ++++++++++++ views/settings.go | 7 ++++--- 12 files changed, 84 insertions(+), 62 deletions(-) create mode 100644 views/render.go diff --git a/config/main.go b/config/main.go index 48615c4..5e6be25 100644 --- a/config/main.go +++ b/config/main.go @@ -17,6 +17,8 @@ type Config struct { DefaultPassword string // Controls the "secure" option for a token cookie. SecureCookie bool `default:"true"` + + Title string `default:"Phoenix"` } func GetConfig() (*Config, error) { diff --git a/readme.md b/readme.md index 11765d2..bf841f9 100644 --- a/readme.md +++ b/readme.md @@ -28,6 +28,11 @@ Service settings can be set through environment variables. | P_DEFAULTPASSWORD | Data for the first user. | | | P_SECURECOOKIE | Controls the "secure" option for a token cookie. | `true` | +Appearance settings: +| Variable | Description | Default | +| --- | --- | --- | +| P_TITLE | Website title | Phoenix | + ## Docker-compose example ```yml services: diff --git a/templates/head.html.tmpl b/templates/head.html.tmpl index 5ee2705..a9013e8 100644 --- a/templates/head.html.tmpl +++ b/templates/head.html.tmpl @@ -1,5 +1,5 @@ {{define "head"}} -Phoenix +{{.WebsiteTitle}} diff --git a/templates/index.html.tmpl b/templates/index.html.tmpl index c198b95..39ebe84 100644 --- a/templates/index.html.tmpl +++ b/templates/index.html.tmpl @@ -6,7 +6,7 @@
-

Phoenix

+

{{.WebsiteTitle}}

{{if not .groups}}

You don't have any links. diff --git a/views/auth.go b/views/auth.go index 8d548e5..5b59025 100644 --- a/views/auth.go +++ b/views/auth.go @@ -15,14 +15,14 @@ import ( const TOKEN_LIFETIME_IN_SECONDS = 60 * 60 * 24 * 30 -func ShowRegistrationForm(db *gorm.DB) gin.HandlerFunc { +func ShowRegistrationForm(cfg *config.Config, 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")) + ShowError(ctx, cfg, errors.New("At least 1 user already exists")) return } - ctx.HTML(http.StatusOK, "auth.html.tmpl", gin.H{ + Render(ctx, cfg, 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", @@ -31,13 +31,15 @@ func ShowRegistrationForm(db *gorm.DB) gin.HandlerFunc { } } -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", - "formAction": "/api/users/signin", - }) +func ShowLoginForm(cfg *config.Config) gin.HandlerFunc { + return func(ctx *gin.Context) { + Render(ctx, cfg, http.StatusOK, "auth.html.tmpl", gin.H{ + "title": "Sign in", + "description": "Authorization is required to view this page.", + "button": "Sign in", + "formAction": "/api/users/signin", + }) + } } // Requires the user to log in before viewing the page. @@ -79,7 +81,7 @@ func AuthMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { // Generate access token. token, err := GetJWTToken(cfg) if err != nil { - ShowError(ctx, err) + ShowError(ctx, cfg, err) return } SetTokenCookie(ctx, token, cfg) @@ -99,7 +101,7 @@ func AuthMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { 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) + ShowError(ctx, cfg, err) return } SetTokenCookie(ctx, newToken, cfg) @@ -118,7 +120,7 @@ func GetJWTToken(cfg *config.Config) (string, error) { 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")) + ShowError(ctx, cfg, errors.New("At least 1 user already exists")) return } @@ -127,14 +129,14 @@ func CreateUser(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { password := ctx.PostForm("password") _, err := database.CreateAdmin(db, username, password) if err != nil { - ShowError(ctx, err) + ShowError(ctx, cfg, err) return } // Generate access token. token, err := GetJWTToken(cfg) if err != nil { - ShowError(ctx, err) + ShowError(ctx, cfg, err) return } SetTokenCookie(ctx, token, cfg) @@ -151,14 +153,14 @@ func AuthorizeUser(db *gorm.DB, cfg *config.Config) gin.HandlerFunc { password := ctx.PostForm("password") _, err := database.AuthorizeAdmin(db, username, password) if err != nil { - ShowError(ctx, err) + ShowError(ctx, cfg, err) return } // Generate an access token. token, err := GetJWTToken(cfg) if err != nil { - ShowError(ctx, err) + ShowError(ctx, cfg, err) return } SetTokenCookie(ctx, token, cfg) diff --git a/views/errors.go b/views/errors.go index ac6d456..3265114 100644 --- a/views/errors.go +++ b/views/errors.go @@ -2,16 +2,13 @@ package views import ( "github.com/gin-gonic/gin" + "github.com/ordinary-dev/phoenix/config" "net/http" ) -func ShowError(ctx *gin.Context, err error) { - ctx.HTML( - http.StatusBadRequest, - "error.html.tmpl", - gin.H{ - "error": err.Error(), - }, - ) +func ShowError(ctx *gin.Context, cfg *config.Config, err error) { + Render(ctx, cfg, http.StatusBadRequest, "error.html.tmpl", gin.H{ + "error": err.Error(), + }) ctx.Abort() } diff --git a/views/groups.go b/views/groups.go index ce1d7db..2584e0d 100644 --- a/views/groups.go +++ b/views/groups.go @@ -3,20 +3,21 @@ package views import ( "fmt" "github.com/gin-gonic/gin" + "github.com/ordinary-dev/phoenix/config" "github.com/ordinary-dev/phoenix/database" "gorm.io/gorm" "net/http" "strconv" ) -func CreateGroup(db *gorm.DB) gin.HandlerFunc { +func CreateGroup(cfg *config.Config, 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) + ShowError(ctx, cfg, result.Error) return } @@ -25,23 +26,23 @@ func CreateGroup(db *gorm.DB) gin.HandlerFunc { } } -func UpdateGroup(db *gorm.DB) gin.HandlerFunc { +func UpdateGroup(cfg *config.Config, 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) + ShowError(ctx, cfg, err) return } var group database.Group if result := db.First(&group, id); result.Error != nil { - ShowError(ctx, result.Error) + ShowError(ctx, cfg, result.Error) return } group.Name = ctx.PostForm("groupName") if result := db.Save(&group); result.Error != nil { - ShowError(ctx, result.Error) + ShowError(ctx, cfg, result.Error) return } @@ -50,16 +51,16 @@ func UpdateGroup(db *gorm.DB) gin.HandlerFunc { } } -func DeleteGroup(db *gorm.DB) gin.HandlerFunc { +func DeleteGroup(cfg *config.Config, 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) + ShowError(ctx, cfg, err) return } if result := db.Delete(&database.Group{}, id); result.Error != nil { - ShowError(ctx, result.Error) + ShowError(ctx, cfg, result.Error) return } diff --git a/views/index.go b/views/index.go index c0ad2be..f027378 100644 --- a/views/index.go +++ b/views/index.go @@ -2,12 +2,13 @@ package views import ( "github.com/gin-gonic/gin" + "github.com/ordinary-dev/phoenix/config" "github.com/ordinary-dev/phoenix/database" "gorm.io/gorm" "net/http" ) -func ShowMainPage(db *gorm.DB) gin.HandlerFunc { +func ShowMainPage(cfg *config.Config, db *gorm.DB) gin.HandlerFunc { return func(ctx *gin.Context) { // Get a list of groups with links var groups []database.Group @@ -17,11 +18,11 @@ func ShowMainPage(db *gorm.DB) gin.HandlerFunc { Find(&groups) if result.Error != nil { - ShowError(ctx, result.Error) + ShowError(ctx, cfg, result.Error) return } - ctx.HTML(http.StatusOK, "index.html.tmpl", gin.H{ + Render(ctx, cfg, http.StatusOK, "index.html.tmpl", gin.H{ "groups": groups, }) } diff --git a/views/links.go b/views/links.go index bba20d7..f1b1293 100644 --- a/views/links.go +++ b/views/links.go @@ -3,17 +3,18 @@ package views import ( "fmt" "github.com/gin-gonic/gin" + "github.com/ordinary-dev/phoenix/config" "github.com/ordinary-dev/phoenix/database" "gorm.io/gorm" "net/http" "strconv" ) -func CreateLink(db *gorm.DB) gin.HandlerFunc { +func CreateLink(cfg *config.Config, 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) + ShowError(ctx, cfg, err) return } @@ -29,7 +30,7 @@ func CreateLink(db *gorm.DB) gin.HandlerFunc { link.Icon = &icon } if result := db.Create(&link); result.Error != nil { - ShowError(ctx, result.Error) + ShowError(ctx, cfg, result.Error) return } @@ -38,17 +39,17 @@ func CreateLink(db *gorm.DB) gin.HandlerFunc { } } -func UpdateLink(db *gorm.DB) gin.HandlerFunc { +func UpdateLink(cfg *config.Config, 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) + ShowError(ctx, cfg, err) return } var link database.Link if result := db.First(&link, id); result.Error != nil { - ShowError(ctx, err) + ShowError(ctx, cfg, err) return } @@ -61,7 +62,7 @@ func UpdateLink(db *gorm.DB) gin.HandlerFunc { link.Icon = &icon } if result := db.Save(&link); result.Error != nil { - ShowError(ctx, result.Error) + ShowError(ctx, cfg, result.Error) return } @@ -70,16 +71,16 @@ func UpdateLink(db *gorm.DB) gin.HandlerFunc { } } -func DeleteLink(db *gorm.DB) gin.HandlerFunc { +func DeleteLink(cfg *config.Config, 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) + ShowError(ctx, cfg, err) return } if result := db.Delete(&database.Link{}, id); result.Error != nil { - ShowError(ctx, result.Error) + ShowError(ctx, cfg, result.Error) return } diff --git a/views/main.go b/views/main.go index 327f7db..72e07bd 100644 --- a/views/main.go +++ b/views/main.go @@ -22,10 +22,10 @@ func GetGinEngine(cfg *config.Config, db *gorm.DB) *gin.Engine { engine.Use(SecurityHeadersMiddleware) - engine.GET("/signin", ShowLoginForm) + engine.GET("/signin", ShowLoginForm(cfg)) engine.POST("/api/users/signin", AuthorizeUser(db, cfg)) - engine.GET("/registration", ShowRegistrationForm(db)) + engine.GET("/registration", ShowRegistrationForm(cfg, db)) engine.POST("/api/users", CreateUser(db, cfg)) // This group requires authorization before viewing. @@ -33,31 +33,31 @@ func GetGinEngine(cfg *config.Config, db *gorm.DB) *gin.Engine { protected.Use(AuthMiddleware(db, cfg)) // Main page - protected.GET("/", ShowMainPage(db)) + protected.GET("/", ShowMainPage(cfg, db)) - protected.GET("/settings", ShowSettings(db)) + protected.GET("/settings", ShowSettings(cfg, db)) // Create new group - protected.POST("/api/groups", CreateGroup(db)) + protected.POST("/api/groups", CreateGroup(cfg, db)) // Update group // HTML forms cannot be submitted using PUT or PATCH methods without javascript. - protected.POST("/api/groups/:id/put", UpdateGroup(db)) + protected.POST("/api/groups/:id/put", UpdateGroup(cfg, db)) // Delete group // HTML forms cannot be submitted using the DELETE method without javascript. - protected.POST("/api/groups/:id/delete", DeleteGroup(db)) + protected.POST("/api/groups/:id/delete", DeleteGroup(cfg, db)) // Create new link - protected.POST("/api/links", CreateLink(db)) + protected.POST("/api/links", CreateLink(cfg, db)) // Update link. // HTML forms cannot be submitted using PUT or PATCH methods without javascript. - protected.POST("/api/links/:id/put", UpdateLink(db)) + protected.POST("/api/links/:id/put", UpdateLink(cfg, db)) // Delete link // HTML forms cannot be submitted using the DELETE method without javascript. - protected.POST("/api/links/:id/delete", DeleteLink(db)) + protected.POST("/api/links/:id/delete", DeleteLink(cfg, db)) return engine } diff --git a/views/render.go b/views/render.go new file mode 100644 index 0000000..1bdd38a --- /dev/null +++ b/views/render.go @@ -0,0 +1,12 @@ +package views + +import ( + "github.com/gin-gonic/gin" + "github.com/ordinary-dev/phoenix/config" +) + +// Fill in the necessary parameters from the settings and output html. +func Render(ctx *gin.Context, cfg *config.Config, status int, templatePath string, params map[string]any) { + params["WebsiteTitle"] = cfg.Title + ctx.HTML(status, templatePath, params) +} diff --git a/views/settings.go b/views/settings.go index 8379561..bfe1642 100644 --- a/views/settings.go +++ b/views/settings.go @@ -2,12 +2,13 @@ package views import ( "github.com/gin-gonic/gin" + "github.com/ordinary-dev/phoenix/config" "github.com/ordinary-dev/phoenix/database" "gorm.io/gorm" "net/http" ) -func ShowSettings(db *gorm.DB) gin.HandlerFunc { +func ShowSettings(cfg *config.Config, db *gorm.DB) gin.HandlerFunc { return func(ctx *gin.Context) { // Get a list of groups with links var groups []database.Group @@ -17,11 +18,11 @@ func ShowSettings(db *gorm.DB) gin.HandlerFunc { Find(&groups) if result.Error != nil { - ShowError(ctx, result.Error) + ShowError(ctx, cfg, result.Error) return } - ctx.HTML(http.StatusOK, "settings.html.tmpl", gin.H{ + Render(ctx, cfg, http.StatusOK, "settings.html.tmpl", gin.H{ "groups": groups, }) }