Add the ability to change the title

This commit is contained in:
Ivan R. 2023-11-01 23:18:33 +05:00
parent 113d5860ff
commit 00dcd04eb7
No known key found for this signature in database
GPG key ID: 56C7BAAE859B302C
12 changed files with 84 additions and 62 deletions

View file

@ -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) {

View file

@ -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:

View file

@ -1,5 +1,5 @@
{{define "head"}}
<title>Phoenix</title>
<title>{{.WebsiteTitle}}</title>
<meta charset="utf-8" />
<meta name="description" content="A minimalistic start page with your collection of links to important sites." />
<meta name="viewport" content="width=device-width, initial-scale=1" />

View file

@ -6,7 +6,7 @@
</head>
<body>
<div class="page">
<h1>Phoenix</h1>
<h1>{{.WebsiteTitle}}</h1>
{{if not .groups}}
<p>
You don't have any links.

View file

@ -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{
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)

View file

@ -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{
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()
}

View file

@ -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
}

View file

@ -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,
})
}

View file

@ -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
}

View file

@ -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
}

12
views/render.go Normal file
View file

@ -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)
}

View file

@ -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,
})
}