diff --git a/.gitignore b/.gitignore
index 21465f2..3d9342a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ dsn
*.pem
api_key.pem
admin_hash.pem
+create_admin_form.html
+error.log
diff --git a/controllers/admin.go b/controllers/admin.go
index fbabce7..9879084 100644
--- a/controllers/admin.go
+++ b/controllers/admin.go
@@ -1,101 +1,179 @@
package controllers
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .Rawley Fowler, 2022
+*/
+
import (
- "crypto/sha256"
- "fmt"
"net/http"
+ "time"
"github.com/gin-gonic/gin"
"gitlab.com/rawleyifowler/site-rework/models"
+ "gitlab.com/rawleyifowler/site-rework/repos"
"gitlab.com/rawleyifowler/site-rework/utils"
)
var (
- key string = utils.LoadApiKey("api_key.pem")
- admin_hash [32]byte = utils.LoadAdminHash("admin_hash.pem")
- att map[string]uint32 = make(map[string]uint32)
+ api_key string = utils.LoadApiKey("api_key.pem")
)
-// Ensure is logged in
-func AdminOnly() gin.HandlerFunc {
- return func(c *gin.Context) {
- var status uint = http.StatusOK
- k, err := c.Cookie("admin_key")
- if err != nil {
- status = http.StatusForbidden
- }
- fmt.Println(k)
- if k == key && status == http.StatusOK {
- c.Next()
- } else {
- c.HTML(int(status), "forbidden.tmpl", models.Page{Title: " | forbidden"})
- c.Abort()
- return
- }
- }
+type AdminController struct {
+ Repository *repos.AdminRepo
+ BlogRepository *repos.BlogRepo
+ ActiveLogins map[string]int64
+ LoginAttemptsByIp map[string]uint
+}
+
+func NewAdminController(r *repos.AdminRepo) *AdminController {
+ var a AdminController
+ a.Repository = r
+ a.ActiveLogins = map[string]int64{}
+ a.LoginAttemptsByIp = map[string]uint{}
+ go utils.TimeClearMap(a.ActiveLogins, 850)
+ return &a
}
func RegisterAdminGroup(r *gin.RouterGroup) {
+ a := NewAdminController(repos.NewAdminRepo("dsn"))
+ r.Use(a.UserBannedMiddleware)
r.GET("/", utils.ServePage("login.tmpl"))
- r.POST("/login", HandleLogin)
- r.GET("/post", AdminOnly(), utils.ServePage("create_post.tmpl"))
- r.POST("/post", AdminOnly(), CreatePost)
+ r.POST("/login", a.AdminLogin)
+ r.GET("/post", a.AuthMiddleware, utils.ServePage("create_post.tmpl"))
+ r.POST("/post", a.AuthMiddleware, a.CRUDPost)
+ r.POST("/user", a.CreateAdmin)
}
-func HandleLogin(c *gin.Context) {
- if att[c.ClientIP()] > 5 {
- c.HTML(http.StatusForbidden, "forbidden.tmpl", models.Page{Title: " | forbidden"})
+func (a *AdminController) AuthMiddleware(c *gin.Context) {
+ cookie, err := c.Request.Cookie("token")
+ if err != nil {
+ c.HTML(http.StatusForbidden, "forbidden.tmpl", &gin.H{})
+ c.Abort()
return
}
+ if a.ActiveLogins[cookie.Value] == 0 {
+ c.HTML(http.StatusForbidden, "session_revoked.tmpl", &gin.H{})
+ c.Abort()
+ return
+ }
+ c.Next()
+}
+
+func (a *AdminController) UserBannedMiddleware(c *gin.Context) {
+ if a.IpIsBanned(c.ClientIP()) {
+ c.HTML(http.StatusForbidden, "forbidden.tmpl", &gin.H{})
+ c.Abort()
+ return
+ }
+ c.Next()
+}
+
+func (a *AdminController) IpIsBanned(ip string) bool {
+ return a.LoginAttemptsByIp[ip] >= 3
+}
+
+func (a *AdminController) AdminLogin(c *gin.Context) {
err := c.Request.ParseForm()
if err != nil {
- c.HTML(http.StatusForbidden, "forbidden.tmpl", models.Page{Title: " | forbidden"})
+ c.HTML(http.StatusForbidden, "forbidden.tmpl", &gin.H{})
+ a.LoginAttemptsByIp[c.ClientIP()]++
return
}
f := c.Request.Form
- for _, v := range []string{"username", "password"} {
- if !f.Has(v) {
- c.AbortWithStatus(http.StatusNotAcceptable)
- return
- }
+ if !f.Has("username") ||
+ !f.Has("password") {
+ c.HTML(http.StatusNotAcceptable, "admin_success_redirect.tmpl", false)
+ a.LoginAttemptsByIp[c.ClientIP()]++
+ return
}
- str := f.Get("username") + f.Get("password")
- s := sha256.Sum256([]byte(str))
- var success bool
- if s == admin_hash {
- c.SetCookie("admin_key", key, 3600, "/", "rawley.xyz", false, true)
- success = true
- } else {
- att[c.ClientIP()]++
- success = false
+ ad, err := a.Repository.GetAdminByCredentials(f.Get("username"), f.Get("password"))
+ if err != nil ||
+ ad == nil {
+ c.HTML(http.StatusNotAcceptable, "admin_success_redirect.tmpl", false)
+ a.LoginAttemptsByIp[c.ClientIP()]++
+ return
}
- c.HTML(http.StatusOK, "admin_success_redirect.tmpl", struct {
- Success bool
- Title string
- }{Success: success, Title: "login"})
+ a.ActiveLogins[ad.Token] = time.Now().UnixMilli() + (3600 * 1000)
+ c.SetCookie("token", ad.Token, 3600*1000, "/", "rawley.xyz", true, true)
+ c.HTML(http.StatusAccepted, "admin_success_redirect.tmpl", true)
}
-func CreatePost(c *gin.Context) {
+func (a *AdminController) CRUDPost(c *gin.Context) {
err := c.Request.ParseForm()
if err != nil {
- c.AbortWithStatus(http.StatusBadRequest)
+ c.HTML(http.StatusNotAcceptable, "post_success.tmpl", false)
return
}
f := c.Request.Form
- for _, v := range []string{"title", "content", "url"} {
- if !f.Has(v) {
- c.AbortWithStatus(http.StatusNotAcceptable)
- return
- }
+ if f.Get("url") == "" ||
+ f.Get("op") == "" {
+ c.HTML(http.StatusNotAcceptable, "post_success.tmpl", false)
+ return
}
- b := AddBlogPost(&models.BlogPost{
- Title: f.Get("title"),
- Content: f.Get("content"),
+ tempBlogRepo := repos.NewBlogRepo("dsn")
+ tempPost := &models.BlogPost{
Url: f.Get("url"),
- })
- c.HTML(http.StatusOK, "post_success.tmpl", struct {
- Url string
- Success bool
- Title string
- }{Url: f.Get("url"), Success: b, Title: " | post attempt"})
+ Content: f.Get("content"),
+ Title: f.Get("title"),
+ }
+ switch f.Get("op") {
+ case "create":
+ err = tempBlogRepo.CreateBlogPost(tempPost)
+ break
+ case "update":
+ err = tempBlogRepo.UpdateExistingPost(tempPost)
+ break
+ case "delete":
+ err = tempBlogRepo.DeleteExistingPost(tempPost)
+ break
+ default:
+ c.HTML(http.StatusNotAcceptable, "post_success.tmpl", false)
+ return
+ }
+ if err != nil {
+ c.HTML(http.StatusNotAcceptable, "post_success.tmpl", false)
+ return
+ }
+ c.HTML(http.StatusAccepted, "post_success.tmpl", true)
+}
+
+func (a *AdminController) CreateAdmin(c *gin.Context) {
+ err := c.Request.ParseForm()
+ if err != nil {
+ c.HTML(http.StatusNotAcceptable, "forbidden.tmpl", &gin.H{})
+ return
+ }
+ var at models.Administrator
+ f := c.Request.Form
+ if !f.Has("password") ||
+ !f.Has("username") ||
+ !f.Has("api_key") {
+ c.HTML(http.StatusBadRequest, "forbidden.tmpl", &gin.H{})
+ return
+ }
+ at.Username = f.Get("username")
+ at.Password = f.Get("password")
+ if f.Get("api_key") != api_key {
+ c.HTML(http.StatusForbidden, "forbidden.tmpl", &gin.H{})
+ return
+ }
+ err = a.Repository.CreateAdmin(&at)
+ if err != nil {
+ c.HTML(http.StatusForbidden, "forbidden.tmpl", &gin.H{})
+ return
+ }
+ c.HTML(http.StatusAccepted, "post_success.tmpl", true)
}
diff --git a/controllers/admin_test.go b/controllers/admin_test.go
new file mode 100644
index 0000000..b39cffe
--- /dev/null
+++ b/controllers/admin_test.go
@@ -0,0 +1,18 @@
+package controllers
+
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .Rawley Fowler, 2022
+*/
diff --git a/controllers/blog.go b/controllers/blog.go
index ef87381..a5ed30f 100644
--- a/controllers/blog.go
+++ b/controllers/blog.go
@@ -17,158 +17,47 @@ along with this program. If not, see .Rawley Fow
import (
"net/http"
- "strconv"
"github.com/gin-gonic/gin"
- "gitlab.com/rawleyifowler/site-rework/models"
- "gitlab.com/rawleyifowler/site-rework/utils"
- "gorm.io/driver/mysql"
- "gorm.io/gorm"
+ "gitlab.com/rawleyifowler/site-rework/repos"
)
type CommentDto struct {
Url string
}
-var (
- db *gorm.DB
- recentPosters map[string]uint
- captchaVals [2]int
-)
+type BlogController struct {
+ Repository *repos.BlogRepo
+}
+
+func NewBlogController(r *repos.BlogRepo) *BlogController {
+ c := new(BlogController)
+ c.Repository = r
+ return c
+}
func RegisterBlogGroup(r *gin.RouterGroup) {
- // Initialize recent posters cache
- recentPosters = make(map[string]uint)
- // Load dsn and initialize database
- dsn := utils.LoadDSN("dsn")
- var err error
- db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
+ c := NewBlogController(repos.NewBlogRepo("dsn"))
+ r.GET("/", c.IndexBlogPage)
+ r.GET("/post/:url", c.IndividualBlogPage)
+}
+
+func (bc *BlogController) IndexBlogPage(c *gin.Context) {
+ posts, err := bc.Repository.GetAllBlogPosts()
if err != nil {
- panic("Database connection failed...")
- }
- utils.PerformMigrations(db)
- r.GET("/", RenderBlogPage)
- r.GET("/post/:url", RenderIndividualBlogPost)
- r.POST("/post")
- r.POST("/post/comment", CreateComment)
- // Clear the commenters cache every 15 minutes
- go utils.TimedClearMap(&recentPosters, 320000*5)
- go utils.UpdateCaptcha(&captchaVals)
-}
-
-func RenderBlogPage(c *gin.Context) {
- posts := GetAllBlogPosts()
- if posts == nil {
- c.HTML(http.StatusInternalServerError, "internal_server_error.tmpl", models.Page{Title: " | 500"})
- } else {
- c.HTML(http.StatusOK, "blog.tmpl", struct {
- Posts []models.BlogPost
- Title string
- }{Posts: *posts, Title: " | blog"})
- }
-}
-
-func RenderIndividualBlogPost(c *gin.Context) {
- bp := GetBlogPostById(c.Param("url"))
- if bp == nil {
- c.HTML(http.StatusNotFound, "not_found.tmpl", models.Page{Title: "404"})
- return
- } else {
- // Blog posts handle the title themselves
- c.HTML(http.StatusOK, "blog_post.tmpl", struct {
- Post *models.BlogPost
- Captcha [2]int
- }{Post: bp, Captcha: captchaVals})
- }
-}
-
-func CreateComment(c *gin.Context) {
- if c.Request.ParseForm() != nil {
- c.AbortWithStatus(http.StatusNotAcceptable)
+ c.HTML(http.StatusInternalServerError, "internal_server_error.tmpl", &gin.H{})
return
}
- a := []string{c.Request.Form.Get("author"),
- c.Request.Form.Get("content"),
- c.Request.Form.Get("url"),
- c.Request.Form.Get("captcha")}
- // If the length of the comment is great enough, and the comment already exists we can safely assume it is spam.
- if len(a[1]) > 20 && len(*GetCommentsByContent(a[1], a[2])) > 0 {
- c.HTML(http.StatusNotAcceptable, "comment_post_failed.tmpl", CommentDto{Url: a[2]})
- return
- }
- i, err := strconv.ParseInt(a[3], 10, 32)
- if err != nil || int(i) != (captchaVals[0]+captchaVals[1]) {
- c.HTML(http.StatusNotAcceptable, "comment_post_failed.tmpl", CommentDto{Url: a[2]})
- return
- }
- if GetNumberOfRecentPosts(c) >= 2 {
- c.HTML(http.StatusNotAcceptable, "comment_post_spam.tmpl", CommentDto{Url: a[2]})
- return
- }
- for _, v := range a {
- if len(v) == 0 {
- c.HTML(http.StatusNotAcceptable, "comment_post_failed.tmpl", CommentDto{Url: a[2]})
- return
- }
- }
- comment := models.Comment{
- Author: a[0],
- Content: a[1],
- AssociatedPost: a[2],
- }
- db.Create(&comment)
- // Something was wrong with c.ClientIP() with trusted?? Weird.
- recentPosters[c.ClientIP()]++
- // Pass the associated post to the template to add to the href
- c.HTML(http.StatusOK, "comment_post.tmpl", CommentDto{Url: comment.AssociatedPost})
+ c.HTML(http.StatusOK, "blog.tmpl", *posts)
}
-func GetNumberOfRecentPosts(c *gin.Context) uint {
- return recentPosters[c.ClientIP()]
-}
-
-func AddBlogPost(bp *models.BlogPost) bool {
- err := db.Create(bp).Error
- return err == nil
-}
-
-func DeleteBlogPostById(id string) bool {
- err := db.Model(&models.BlogPost{}).Delete(&models.BlogPost{Url: id}).Error
- return err == nil
-}
-
-func GetAllBlogPosts() *[]models.BlogPost {
- var posts []models.BlogPost
- // Select title, date, and url fields from the blog post records an store them in posts.
- // This is so we don't grab the entire blog post when we render them on the overview page. Saves a couple ms.
- err := db.Model(&models.BlogPost{}).Select("title, date, url").Order("date DESC").Scan(&posts).Error
+func (bc *BlogController) IndividualBlogPage(c *gin.Context) {
+ post, err := bc.Repository.GetBlogByUrl(c.Param("url"))
if err != nil {
- return nil
+ c.HTML(http.StatusNotFound, "not_found.tmpl", &gin.H{})
+ return
}
- return &posts
-}
-
-func GetBlogPostById(id string) *models.BlogPost {
- var post models.BlogPost = models.BlogPost{}
- err := db.Model(&post).Where(&models.BlogPost{Url: id}).Find(&post).Error
- if err != nil || post.Equals(&models.BlogPost{}) {
- return nil
- }
- // TODO: Figure out gorm joins!
- // gorm joins are not working at all, just going to do another query to make it work for now.
- err = db.Model(&models.Comment{}).Where(&models.Comment{AssociatedPost: id}).Find(&post.Comments).Error
- if err != nil {
- return nil
- }
- return &post
-}
-
-func GetCommentsByContent(content string, url string) *[]models.Comment {
- // This is used to make sure people don't paste the same thing over and over.
- var comments []models.Comment
- err := db.Model(&comments).Where("content like ? and associated_post like ?", "%"+content[1:]+"%", url).Scan(&comments).Error
- if err != nil {
- return &[]models.Comment{}
- }
- return &comments
+ // TODO: Re implement captcha here. It is already included on the blog post model, though not in the database.
+ // The idea is to generate a captcha for each post, and index them by title. The async service should then update each captcha every hour.
+ c.HTML(http.StatusOK, "blog_post.tmpl", post)
}
diff --git a/controllers/blog_test.go b/controllers/blog_test.go
index 2d32936..b39cffe 100644
--- a/controllers/blog_test.go
+++ b/controllers/blog_test.go
@@ -1 +1,18 @@
package controllers
+
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .Rawley Fowler, 2022
+*/
diff --git a/controllers/comment_test.go b/controllers/comment_test.go
deleted file mode 100644
index 2d32936..0000000
--- a/controllers/comment_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package controllers
diff --git a/go.mod b/go.mod
index aced78f..94b2dd1 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.15
require (
github.com/gin-gonic/gin v1.7.7
+ github.com/google/uuid v1.3.0 // indirect
gorm.io/driver/mysql v1.2.3
gorm.io/gorm v1.22.5
)
diff --git a/go.sum b/go.sum
index 6946f07..ab7375a 100644
--- a/go.sum
+++ b/go.sum
@@ -16,6 +16,8 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
diff --git a/models/administrator.go b/models/administrator.go
new file mode 100644
index 0000000..ceb555f
--- /dev/null
+++ b/models/administrator.go
@@ -0,0 +1,11 @@
+package models
+
+import "gorm.io/gorm"
+
+type Administrator struct {
+ gorm.Model
+ Id uint `json:"id" gorm:"primaryKey AUTO_INCREMENT"`
+ Token string `json:"token" gorm:"unique"`
+ Username string `gorm:"unique"`
+ Password string
+}
diff --git a/models/blog_post.go b/models/blog_post.go
index f270508..af44b6d 100644
--- a/models/blog_post.go
+++ b/models/blog_post.go
@@ -22,11 +22,11 @@ import (
type BlogPost struct {
gorm.Model
- Url string `json:"url" gorm:"primaryKey"`
- Title string `json:"title" gorm:"unique"`
- Date string `json:"date" gorm:"default:NOW()"`
- Content string `json:"content"`
- Comments []Comment `gorm:"foreignKey:AssociatedPost;references:Url;type:varchar(191)"`
+ Url string `json:"url" gorm:"primaryKey"`
+ Title string `json:"title" gorm:"unique"`
+ Date string `json:"date" gorm:"default:NOW()"`
+ Content string `json:"content"`
+ Captcha [2]int `gorm:"-"`
}
func (b BlogPost) Equals(m *BlogPost) bool {
diff --git a/repos/admin_repo.go b/repos/admin_repo.go
new file mode 100644
index 0000000..6b333c9
--- /dev/null
+++ b/repos/admin_repo.go
@@ -0,0 +1,93 @@
+package repos
+
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .Rawley Fowler, 2022
+*/
+
+import (
+ "crypto/sha256"
+ "errors"
+ "fmt"
+
+ "github.com/google/uuid"
+ "gitlab.com/rawleyifowler/site-rework/models"
+ "gitlab.com/rawleyifowler/site-rework/utils"
+ "gorm.io/gorm"
+)
+
+type AdminRepo struct {
+ DB *gorm.DB
+}
+
+func NewAdminRepo(dsnPath string) *AdminRepo {
+ c := AdminRepo{}
+ var err error
+ c.DB, err = utils.CreateDatabase(utils.LoadDSN(dsnPath))
+ if err != nil {
+ panic(err)
+ }
+ c.DB.AutoMigrate(&models.Administrator{})
+ return &c
+}
+
+func (ar *AdminRepo) CreateAdmin(a *models.Administrator) error {
+ if a.Password == "" ||
+ a.Username == "" {
+ return errors.New("Administrators must have a valid username and password")
+ }
+ if len(a.Password) < 8 {
+ return errors.New("Passwords must have a length of atleast 8")
+ }
+ a.Token = uuid.New().String()
+ a.Password = fmt.Sprintf("%x", string(sha256.New().Sum([]byte(a.Password))))
+ err := ar.DB.Table("administrators").Create(a).Error
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (ar *AdminRepo) GetAdminByToken(k string) (*models.Administrator, error) {
+ if k == "" {
+ return nil, errors.New("Session key cannot be blank")
+ }
+ if len(k) != 36 {
+ return nil, errors.New("Valid sessions keys are always 36 byte utf-8 strings")
+ }
+ var a *models.Administrator
+ err := ar.DB.Table("administrators").Where(&models.Administrator{Token: k}).Scan(a).Error
+ if err != nil {
+ return nil, err
+ }
+ return a, nil
+}
+
+func (ar *AdminRepo) GetAdminByCredentials(uname, pword string) (*models.Administrator, error) {
+ if uname == "" ||
+ pword == "" {
+ return nil, errors.New("Username or password mustn't be empty")
+ }
+ if len(pword) < 8 {
+ return nil, errors.New("Password length must be no less than 8")
+ }
+ pword = fmt.Sprintf("%x", string(sha256.New().Sum([]byte(pword))))
+ var a models.Administrator
+ err := ar.DB.Table("administrators").Where(&models.Administrator{Username: uname, Password: pword}).First(&a).Error
+ if err != nil {
+ return nil, err
+ }
+ return &a, nil
+}
diff --git a/repos/admin_repo_test.go b/repos/admin_repo_test.go
new file mode 100644
index 0000000..0d65e9a
--- /dev/null
+++ b/repos/admin_repo_test.go
@@ -0,0 +1,67 @@
+package repos
+
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .Rawley Fowler, 2022
+*/
+
+import (
+ "testing"
+
+ "gitlab.com/rawleyifowler/site-rework/models"
+)
+
+var test_admin_repo = NewAdminRepo("dsn")
+
+func TestCreateAdmin(t *testing.T) {
+ err := test_admin_repo.CreateAdmin(&models.Administrator{})
+ if err == nil {
+ t.Fatalf("Expected: You cannot create an admin with empty credentials")
+ }
+ err = test_admin_repo.CreateAdmin(&models.Administrator{Username: "", Password: "12345678"})
+ if err == nil {
+ t.Fatalf("Expected: You cannot create an admin with empty credentials")
+ }
+ err = test_admin_repo.CreateAdmin(&models.Administrator{Username: "12345678", Password: ""})
+ if err == nil {
+ t.Fatalf("Expected: You cannot create an admin with empty credentials")
+ }
+ err = test_admin_repo.CreateAdmin(&models.Administrator{Username: "valid_username", Password: "invalid"})
+ if err == nil {
+ t.Fatalf("Expected: You cannot create an admin with a password less than 8 characters")
+ }
+}
+
+func TestGetAdminByToken(t *testing.T) {
+ _, err := test_admin_repo.GetAdminByToken("")
+ if err == nil {
+ t.Fatalf("Expected: Error should be thrown when an empty session key is provided")
+ }
+}
+
+func TestGetAdminByCredentials(t *testing.T) {
+ _, err := test_admin_repo.GetAdminByCredentials("", "")
+ if err == nil {
+ t.Fatalf("Expected: Error should be thrown if empty credentials are given")
+ }
+ _, err = test_admin_repo.GetAdminByCredentials("1234", "")
+ if err == nil {
+ t.Fatalf("Expected: Error should be thrown if empty credentials are given")
+ }
+ _, err = test_admin_repo.GetAdminByCredentials("", "123456789")
+ if err == nil {
+ t.Fatalf("Expected: Error should be thrown if empty credentials are given")
+ }
+}
diff --git a/repos/blog_repo.go b/repos/blog_repo.go
index 2b0d3a9..986c144 100644
--- a/repos/blog_repo.go
+++ b/repos/blog_repo.go
@@ -1 +1,107 @@
package repos
+
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .Rawley Fowler, 2022
+*/
+
+import (
+ "errors"
+
+ "gitlab.com/rawleyifowler/site-rework/models"
+ "gitlab.com/rawleyifowler/site-rework/utils"
+ "gorm.io/gorm"
+)
+
+type BlogRepo struct {
+ DB *gorm.DB
+ initialized bool
+}
+
+func NewBlogRepo(dsnPath string) *BlogRepo {
+ b := BlogRepo{}
+ var err error
+ b.DB, err = utils.CreateDatabase(utils.LoadDSN(dsnPath))
+ if err != nil {
+ panic(err)
+ }
+ b.DB.AutoMigrate(&models.BlogPost{})
+ return &b
+}
+
+func (b *BlogRepo) CreateBlogPost(p *models.BlogPost) error {
+ if p == nil {
+ return errors.New("Blog post must not be nil")
+ }
+ if p.Title == "" ||
+ p.Content == "" ||
+ p.Url == "" {
+ return errors.New("Blog posts must be fully formed")
+ }
+ err := b.DB.Table("blog_posts").Create(p).Error
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (b *BlogRepo) GetBlogByUrl(url string) (*models.BlogPost, error) {
+ if url == "" {
+ return nil, errors.New("Cannot query by empty value")
+ }
+ var post models.BlogPost
+ err := b.DB.Table("blog_posts").Where(&models.BlogPost{Url: url}).Scan(&post).Error
+ if err != nil {
+ return nil, err
+ }
+ return &post, nil
+}
+
+func (b *BlogRepo) GetAllBlogPosts() (*[]models.BlogPost, error) {
+ posts := new([]models.BlogPost)
+ err := b.DB.Table("blog_posts").Scan(posts).Error
+ if err != nil {
+ return nil, errors.New("Could not load all posts from database")
+ }
+ return posts, nil
+}
+
+func (b *BlogRepo) UpdateExistingPost(p *models.BlogPost) error {
+ if p == nil {
+ return errors.New("Cannot update with nil value for post")
+ }
+ if p.Url == "" {
+ return errors.New("Url must exist to update a blog post record")
+ }
+ err := b.DB.Table("blog_posts").Save(p).Error
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (b *BlogRepo) DeleteExistingPost(p *models.BlogPost) error {
+ if p == nil {
+ return errors.New("Cannot delete with nil value for post")
+ }
+ if p.Url == "" {
+ return errors.New("Url must exist to delete a blog post record")
+ }
+ err := b.DB.Table("blog_posts").Delete(p).Error
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/repos/blog_repo_test.go b/repos/blog_repo_test.go
index e69de29..a71847b 100644
--- a/repos/blog_repo_test.go
+++ b/repos/blog_repo_test.go
@@ -0,0 +1,50 @@
+package repos
+
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .Rawley Fowler, 2022
+*/
+
+import (
+ "testing"
+
+ "gitlab.com/rawleyifowler/site-rework/models"
+)
+
+var test_blog_repo = NewBlogRepo("dsn")
+
+func TestGetBlogByUrl(t *testing.T) {
+ _, err := test_blog_repo.GetBlogByUrl("")
+ if err == nil {
+ t.Fatalf("Expected: Empty values of url for get blog by url should return an error")
+ }
+}
+
+func TestCreateBlogPost(t *testing.T) {
+ err := test_blog_repo.CreateBlogPost(&models.BlogPost{})
+ if err == nil {
+ t.Fatalf("Expected: Blog posts must be fully formed")
+ }
+
+ err = test_blog_repo.CreateBlogPost(&models.BlogPost{Title: "abc"})
+ if err == nil {
+ t.Fatalf("Expected: Blog posts must be fully formed, title, content, url")
+ }
+
+ err = test_blog_repo.CreateBlogPost(nil)
+ if err == nil {
+ t.Fatalf("Expected: Cannot create blog posts from nil values")
+ }
+}
diff --git a/repos/comment_repo.go b/repos/comment_repo.go
deleted file mode 100644
index b493439..0000000
--- a/repos/comment_repo.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package repos
-
-import (
- "errors"
-
- "gitlab.com/rawleyifowler/site-rework/models"
- "gitlab.com/rawleyifowler/site-rework/utils"
- "gorm.io/driver/mysql"
- "gorm.io/gorm"
-)
-
-type CommentRepo struct {
- DB *gorm.DB
- Initialized bool
-}
-
-func NewCommentRepo(dsnPath string) (*CommentRepo, error) {
- c := CommentRepo{}
- err := c.Initialize(dsnPath)
- if err != nil {
- return &c, err
- }
- return &c, nil
-}
-
-func (c *CommentRepo) Initialize(path string) error {
- dsn := utils.LoadDSN(path)
- var err error
- c.DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
- if err != nil {
- return err
- }
- c.Initialized = true
- return nil
-}
-
-func (c *CommentRepo) GetCommentById(id uint) (*models.Comment, error) {
- if id == 0 {
- return nil, nil
- }
- var comm models.Comment
- c.DB.Where(&models.Comment{Id: id}).First(&comm)
- return &comm, nil
-}
-
-func (c *CommentRepo) CreateComment(comm *models.Comment) error {
- if comm == nil {
- return errors.New("nil comment")
- }
- err := c.DB.Create(comm).Error
- if err != nil {
- return err
- }
- // nil return means no errors, yay!
- return nil
-}
-
-func (c *CommentRepo) GetCommentsByAssociatedPost(title string) (*[]models.Comment, error) {
- if len(title) < 1 {
- return nil, errors.New("empty associated post title")
- }
- var comms []models.Comment
- err := c.DB.Where(&models.Comment{AssociatedPost: title}).Scan(&comms).Error
- if err != nil {
- return nil, err
- }
- return &comms, nil
-}
-
-func (c *CommentRepo) GetCommentsByAuthor(auth string) (*[]models.Comment, error) {
- if len(auth) < 1 {
- return nil, errors.New("empty author name")
- }
- var comms []models.Comment
- err := c.DB.Where(&models.Comment{Author: auth}).Scan(&comms).Error
- if err != nil {
- return nil, err
- }
- return &comms, nil
-}
diff --git a/repos/comment_repo_test.go b/repos/comment_repo_test.go
deleted file mode 100644
index 07752c1..0000000
--- a/repos/comment_repo_test.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package repos
-
-import (
- "testing"
-
- "gitlab.com/rawleyifowler/site-rework/models"
-)
-
-var cr *CommentRepo
-
-func TestInitialize(t *testing.T) {
- var err error
- cr, err = NewCommentRepo("dsn")
- if err != nil {
-
- }
-}
-
-func TestGetCommentById(t *testing.T) {
- const ID uint = 1
- const AUTHOR string = "Whoops"
- const CONTENT string = "Uhhhhhhhh that wasn't supposed to happen..."
- // This comment will exist forever on the database, it's part of the test blog post.
- x := models.Comment{Id: ID, Author: AUTHOR, Content: CONTENT}
- y, err := cr.GetCommentById(ID)
- if err != nil {
- t.Fatalf("Expected: Comment with id: %d should be found without errors.", ID)
- }
- if x.Id != y.Id ||
- x.Author != y.Author ||
- x.Content != y.Content {
- t.Fatalf("Expected(left expected, right result):\nid -> %d == %d\nauthor -> %s == %s\ncontent -> %s == %s",
- ID, y.Id, AUTHOR, y.Author, CONTENT, y.Content)
- }
- _, err := cr.GetCommentById(0)
- if err == nil {
- t.Fatalf("Expected: Values of less than or equal to 0 are considered illegal values for comment id")
- }
-}
-
-func TestGetCommentsByAssociatedPost(t *testing.T) {
- _, err := cr.GetCommentsByAssociatedPost("")
- if err == nil {
- t.Fatalf("Expected: Empty values are considered illegal for associated post")
- }
- // Wild associated post should return empty array
- post, err := cr.GetCommentsByAssociatedPost("kjdoJDOWIJWKIJALSKAJSAKIJDOIAj")
- if len(*post) > 0 {
- t.Fatalf("Expected: Should return empty array")
- }
- if err != nil {
- t.Fatalf("Expected: Non existant post should not throw error, just empty array")
- }
-}
-
-func TestGetCommentsByAuthor(t *testing.T) {
- _, err := cr.GetCommentsByAuthor("")
- if err == nil {
- t.Fatalf("Expected: An empty author value should throw an error")
- }
-}
-
-func TestCreateComment(t *testing.T) {
- err := cr.CreateComment(nil)
- if err == nil {
- t.Fatalf("Expected: Nil values should result in errors when trying to create comments")
- }
- err = cr.CreateComment(&models.Comment{})
- if err == nil {
- t.Fatalf("Expected: Empty or incomplete models should result in errors.")
- }
- err = cr.CreateComment(&models.Comment{Author: "Chuck"})
- if err == nil {
- t.Fatalf("Expected: Empty or incomplete models should result in errors.")
- }
-}
diff --git a/static/index.css b/static/index.css
index d8a7d16..157eb2c 100644
--- a/static/index.css
+++ b/static/index.css
@@ -164,13 +164,9 @@ a:hover:after {
content: "' ";
}
-.comment {
- margin-top: 3px;
- margin-bottom: 3px;
- border: 2px solid #454545;
-}
-
-.comment-body {
- background-color: rgba(222, 222, 222, 0.5);
- word-wrap: break-word;
+@media (prefers-color-scheme: dark) {
+ body {
+ color: #fafafa;
+ background-color: #242124;
+ }
}
diff --git a/templates/admin_success_redirect.tmpl b/templates/admin_success_redirect.tmpl
index 8a0cc7f..1ab975a 100644
--- a/templates/admin_success_redirect.tmpl
+++ b/templates/admin_success_redirect.tmpl
@@ -1,9 +1,19 @@
-{{ template "header.tmpl" . }}
-{{ if .Success }}
+
+
+
+
+
+
+
+ admin login
+
+
+{{ template "nav.tmpl" . }}
+{{ if . }}
Login was successful !
Click here to go to the post creation area.
{{ else }}
- Login was not successful!
+ Login was not successful...
Click here to return to the login page to try again.
{{ end }}
diff --git a/templates/blog.tmpl b/templates/blog.tmpl
index 5e0175c..9b33227 100644
--- a/templates/blog.tmpl
+++ b/templates/blog.tmpl
@@ -1,6 +1,16 @@
-{{ template "header.tmpl" . }}
+
+
+
+
+
+
+
+ rawley.xyz | blog
+
+
+ {{ template "nav.tmpl" . }}
Blog posts
-{{ range $val := .Posts }}
+{{ range $val := . }}
{{ $val.Title }}
diff --git a/templates/blog_post.tmpl b/templates/blog_post.tmpl
index abb3363..64de20c 100644
--- a/templates/blog_post.tmpl
+++ b/templates/blog_post.tmpl
@@ -5,33 +5,15 @@
-
{{ .Post.Title }}
+
{{ .Title }}
{{ template "nav.tmpl" . }}
-
{{ .Post.Title }}
-
{{ .Post.Content | UnescapeHTML }}
-
Comments
-{{ range $comment := .Post.Comments }}
-
-{{ else }}
-
No comments...
-{{ end }}
-
Leave a comment!
-
-
+
{{ .Title }}
+
{{ .Content | UnescapeHTML }}
+
+
+This site used to have comments. Unfortunatley maintaining a comment system that can resist bots, spammers, and other hooligans is quite tiresome. Until I figure out a way to efficiently handle comments, they are disabled. Sorry for the inconvenience.
+
{{ template "footer_no_banners.tmpl" . }}
diff --git a/templates/comment_post.tmpl b/templates/comment_post.tmpl
deleted file mode 100644
index 6e22141..0000000
--- a/templates/comment_post.tmpl
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
Comment success
-
-
- {{ template "nav.tmpl" . }}
-
Your comment is being posted...
-
You will be redirected or, click here to return to the blog post.
-
-
-
-
diff --git a/templates/comment_post_failed.tmpl b/templates/comment_post_failed.tmpl
deleted file mode 100644
index f43f493..0000000
--- a/templates/comment_post_failed.tmpl
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
Comment failed
-
-
- {{ template "nav.tmpl" . }}
-
Your comment failed to be posted...
-
You will be redirected or, click here to return to the blog post.
-
-
Your comment failed for one of these reasons:
-
-
- You failed the captcha.
- Your post had links in it.
- The name on your comment was "rawley" or "admin".
- You're spamming!
-
-
-
diff --git a/templates/create_post.tmpl b/templates/create_post.tmpl
index bdb93d4..f6c24e9 100644
--- a/templates/create_post.tmpl
+++ b/templates/create_post.tmpl
@@ -1,10 +1,26 @@
-{{ template "header.tmpl" . }}
+
+
+
+
+
+
+
+
crud post
+
+
+{{ template "nav.tmpl" . }}
Create post
-{{ template "footer.tmpl" . }}
+
+
diff --git a/templates/forbidden.tmpl b/templates/forbidden.tmpl
index 24d0eca..b5f1f7c 100644
--- a/templates/forbidden.tmpl
+++ b/templates/forbidden.tmpl
@@ -1,4 +1,14 @@
-{{ template "header.tmpl" . }}
+
+
+
+
+
+
+
+
forbidden
+
+
+{{ template "nav.tmpl" . }}
403 You're not allowed here >:(
Click here to return home.
diff --git a/templates/index.tmpl b/templates/index.tmpl
index f1d39c7..7790ea3 100644
--- a/templates/index.tmpl
+++ b/templates/index.tmpl
@@ -4,7 +4,7 @@
I am an independent software engineer, and self proclaimed systems administrator from Canada. Currently, I am pursuing a degree in Computer Science, with hopes of participating in a graduate program later down the road.
- I am a GNU/Linux and OpenBSD enthusiast. You'll find me active in the Debian Linux, Void Linux and OpenBSD communities. My favourite programming languages are Go and Clojure. Currently, I am working on my website , and an open-source community based blogging network called xrn . You can check out my work on GitLab .
+ I am a GNU/Linux and OpenBSD enthusiast. You'll find me active in the Debian Linux, Void Linux and OpenBSD communities. My favourite programming languages are Go and Clojure. Currently, I am working on my website , and an open-source community based blogging network called xrn . All of my work is open source, you can check it out on my GitLab .
When I'm not programming, I'm either reading, cooking, playing table-top games, or walking my dog.
diff --git a/templates/internal_server_error.tmpl b/templates/internal_server_error.tmpl
index c3ab57e..b876eb9 100644
--- a/templates/internal_server_error.tmpl
+++ b/templates/internal_server_error.tmpl
@@ -1,4 +1,15 @@
-{{ template "header.tmpl" . }}
+
+
+
+
+
+
+
+
internal server error
+
+
+{{ template "nav.tmpl" . }}
500 Internal server error :(
Click here to return home.
-{{ template "footer.tmpl" . }}
+
+
diff --git a/templates/login.tmpl b/templates/login.tmpl
index 63d24a7..d40cd1f 100644
--- a/templates/login.tmpl
+++ b/templates/login.tmpl
@@ -5,4 +5,4 @@
Login
-{{ template "footer.tmpl" . }}
+{{ template "footer_no_banners.tmpl" . }}
diff --git a/templates/not_found.tmpl b/templates/not_found.tmpl
index 1cccbaf..1d66f13 100644
--- a/templates/not_found.tmpl
+++ b/templates/not_found.tmpl
@@ -1,7 +1,19 @@
-{{ template "header.tmpl" . }}
+
+
+
+
+
+
+
+
not found
+
+
+{{ template "nav.tmpl" . }}
+
404 Not found :(
Click here to return home.
-{{ template "footer.tmpl" . }}
+
+
diff --git a/templates/post_success.tmpl b/templates/post_success.tmpl
index 0f4c151..eaf3977 100644
--- a/templates/post_success.tmpl
+++ b/templates/post_success.tmpl
@@ -1,11 +1,20 @@
-{{ template "header.tmpl" . }}
-{{ if .Success }}
-
Post created successfully!
-
Click here to view the post.
-
Click here to return to the posting page.
+
+
+
+
+
+
+
+
forbidden
+
+
+{{ template "nav.tmpl" . }}
+{{ if . }}
+
Operation performced successfuly!
+
Click here to return to the management page.
{{ else }}
-
Post failed to be created...
-
Click here to return to the posting page.
+
Operation failed to perform...
+
Click here to return to the management page.
{{ end }}
diff --git a/templates/comment_post_spam.tmpl b/templates/session_revoked.tmpl
similarity index 59%
rename from templates/comment_post_spam.tmpl
rename to templates/session_revoked.tmpl
index cb5323d..eaefe05 100644
--- a/templates/comment_post_spam.tmpl
+++ b/templates/session_revoked.tmpl
@@ -1,16 +1,15 @@
-
-
+
-
Comment spam
-
-
- {{ template "nav.tmpl" . }}
-
You've commented too many times, too quickly. Your comment will not be posted.
-
Click here to return to the post.
+
session revoked
+
+
+{{ template "nav.tmpl" . }}
+
403 Your session no longer exists
+
Click here to return home.
diff --git a/utils/admin_hash.go b/utils/admin_hash.go
deleted file mode 100644
index 4f39a2e..0000000
--- a/utils/admin_hash.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package utils
-
-import (
- "bufio"
- "os"
-)
-
-func LoadAdminHash(path string) [32]byte {
- file, err := os.Open(path)
- if err != nil {
- panic("Could not find admin hash...")
- }
- defer file.Close()
- reader := bufio.NewScanner(file)
- // The dsn should be the first line of the file
- if reader.Scan() {
- var buff [32]byte
- for i, b := range reader.Bytes() {
- buff[i] = b
- }
- return buff
- }
- panic(path + " is empty...")
-}
diff --git a/utils/api_key.go b/utils/api_key.go
index 34b827e..eff08e4 100644
--- a/utils/api_key.go
+++ b/utils/api_key.go
@@ -16,6 +16,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.Rawley Fowler, 2022
*/
+
import (
"bufio"
"os"
@@ -23,10 +24,10 @@ import (
func LoadApiKey(path string) string {
file, err := os.Open(path)
- defer file.Close()
if err != nil {
- panic("Could not find file: " + path)
+ panic("Could not find DSN for database...")
}
+ defer file.Close()
reader := bufio.NewScanner(file)
if reader.Scan() {
return reader.Text()
diff --git a/utils/captcha.go b/utils/captcha.go
deleted file mode 100644
index 54eb589..0000000
--- a/utils/captcha.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package utils
-
-import (
- "math/rand"
- "time"
-)
-
-func GenerateCaptchaValues() (int, int) {
- return rand.Intn(100), rand.Intn(100)
-}
-
-func UpdateCaptcha(captcha *[2]int) {
- for {
- captcha[0], captcha[1] = GenerateCaptchaValues()
- time.Sleep(30 * time.Minute)
- }
-}
diff --git a/utils/migrations.go b/utils/db_creator.go
similarity index 77%
rename from utils/migrations.go
rename to utils/db_creator.go
index be17039..0df5459 100644
--- a/utils/migrations.go
+++ b/utils/db_creator.go
@@ -1,4 +1,5 @@
package utils
+
/*
Copyright (C) 2022 Rawley Fowler
@@ -15,13 +16,17 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.Rawley Fowler, 2022
*/
+
import (
- "gitlab.com/rawleyifowler/site-rework/models"
+ "gorm.io/driver/mysql"
"gorm.io/gorm"
)
-// Performs migrations of all models
-func PerformMigrations(db *gorm.DB) {
- db.AutoMigrate(&models.BlogPost{})
- db.AutoMigrate(&models.Comment{})
+func CreateDatabase(dsn string) (*gorm.DB, error) {
+ var err error
+ db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
+ if err != nil {
+ return nil, err
+ }
+ return db, nil
}
diff --git a/utils/dsn.go b/utils/dsn.go
index 4771f4f..f340856 100644
--- a/utils/dsn.go
+++ b/utils/dsn.go
@@ -16,6 +16,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.Rawley Fowler, 2022
*/
+
import (
"bufio"
"os"
diff --git a/models/comment.go b/utils/initialize_db.go
similarity index 66%
rename from models/comment.go
rename to utils/initialize_db.go
index 8a07bdc..dd9b440 100644
--- a/models/comment.go
+++ b/utils/initialize_db.go
@@ -1,4 +1,4 @@
-package models
+package utils
/*
Copyright (C) 2022 Rawley Fowler
@@ -16,15 +16,16 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.Rawley Fowler, 2022
*/
+
import (
+ "gorm.io/driver/mysql"
"gorm.io/gorm"
)
-type Comment struct {
- gorm.Model
- Id uint `json:"id" gorm:"primaryKey, AUTO_INCREMENT"`
- Date string `json:"date" gorm:"default:NOW()"`
- Author string `json:"author" form:"author" gorm:"type:varchar(50)"`
- Content string `json:"content" form:"content" gorm:"type:varchar(500)"`
- AssociatedPost string
+func InitializeDatabase(dsn string) *gorm.DB {
+ db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
+ if err != nil {
+ panic("Failed to open database with dsn -> " + dsn)
+ }
+ return db
}
diff --git a/utils/serve_page.go b/utils/serve_page.go
index 2aacc61..aab78ec 100644
--- a/utils/serve_page.go
+++ b/utils/serve_page.go
@@ -1,5 +1,22 @@
package utils
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
.Rawley Fowler, 2022
+*/
+
import (
"net/http"
"strings"
@@ -7,21 +24,14 @@ import (
"github.com/gin-gonic/gin"
)
-// Returns a handler function to serve a page based on the template that is inputted
-func ServePage(temp string) gin.HandlerFunc {
- title := strings.Split(temp, ".")[0]
+func ServePage(t string) gin.HandlerFunc {
+ title := strings.Split(t, ".")[0]
if title == "index" {
- // Index is the home page so we don't care
title = ""
} else {
- // Else title it accordingly
title = " | " + title
}
return func(c *gin.Context) {
- c.HTML(
- http.StatusOK,
- temp,
- struct{ Title string }{Title: title},
- )
+ c.HTML(http.StatusOK, t, struct{ Title string }{Title: title})
}
}
diff --git a/utils/spam_checker.go b/utils/spam_checker.go
deleted file mode 100644
index 20358ed..0000000
--- a/utils/spam_checker.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package utils
-
-// TODO: Implement a spam checking module
-func IsSpam(t string) bool {
- return false
-}
diff --git a/utils/timed_clear.go b/utils/timed_clear.go
deleted file mode 100644
index 2ee4536..0000000
--- a/utils/timed_clear.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package utils
-
-import (
- "strconv"
- "time"
-)
-
-func TimedClearMap(m *map[string]uint, timer uint) {
- for {
- t, err := time.ParseDuration(strconv.FormatUint(uint64(timer), 10) + "ms")
- if err != nil {
- panic("Something went wrong initializing time!!")
- }
- time.Sleep(t)
- for key := range *m {
- delete(*m, key)
- }
- }
-}
diff --git a/utils/timed_clear_map.go b/utils/timed_clear_map.go
new file mode 100644
index 0000000..7ad9286
--- /dev/null
+++ b/utils/timed_clear_map.go
@@ -0,0 +1,29 @@
+package utils
+
+/*
+Copyright (C) 2022 Rawley Fowler
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
.Rawley Fowler, 2022
+*/
+
+import "time"
+
+func TimeClearMap(m map[string]int64, t int64) {
+ time.Sleep(time.Duration(t) * time.Millisecond)
+ for k := range m {
+ if m[k] < time.Now().UnixMilli()+t {
+ delete(m, k)
+ }
+ }
+}