aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/web/handlers/blog.go131
-rw-r--r--cmd/web/handlers/routes.go3
-rw-r--r--internal/dbmigrations.go8
-rw-r--r--internal/models/comment.go12
-rw-r--r--static/app.css4
-rw-r--r--ui/html/pages/post.tmpl.html36
6 files changed, 194 insertions, 0 deletions
diff --git a/cmd/web/handlers/blog.go b/cmd/web/handlers/blog.go
index 4c81669..472b270 100644
--- a/cmd/web/handlers/blog.go
+++ b/cmd/web/handlers/blog.go
@@ -3,6 +3,7 @@ package handlers
import (
"bytes"
"database/sql"
+ "html"
"html/template"
"io"
"log"
@@ -21,16 +22,113 @@ type blogContext struct {
Post models.Post
Rows []models.Post
+ Comments []models.Comment
Name string
IsAuth bool
PagePopulated bool
Offset int
}
+func (ctx *blogContext) comment(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ w.Write([]byte("Method Not Allowed"))
+ return
+ }
+
+ postId := r.URL.Query().Get("id")
+
+ err := r.ParseForm()
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Failed to retrieve form data: %v\n", err)
+ return
+ }
+
+ name := r.PostForm.Get("name")
+ content := html.EscapeString(r.PostForm.Get("comment"))
+
+ insertStmt, err := ctx.db.Prepare("INSERT INTO comments (verified, name, post_id, content) VALUES (FALSE, $1, $2, $3);")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not prepare insert statement to DB: %v\n", err)
+ return
+ }
+ defer insertStmt.Close()
+
+ _, err = insertStmt.Exec(name, postId, content)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not execute comment into DB: %v\n", err)
+ return
+ }
+
+ http.Redirect(w, r, r.Header.Get("Referer"), http.StatusFound)
+ return
+}
+
+func (ctx *blogContext) verifyComment(w http.ResponseWriter, r *http.Request) {
+ commentId := r.URL.Query().Get("id")
+
+ updateStmt, err := ctx.db.Prepare("UPDATE comments SET verified = TRUE WHERE id = $1;")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not prepare update statement to DB: %v\n", err)
+ return
+ }
+ defer updateStmt.Close()
+
+ _, err = updateStmt.Exec(commentId)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not execute to DB: %v\n", err)
+ return
+ }
+
+ http.Redirect(w, r, r.Header.Get("Referer"), http.StatusFound)
+ return
+}
+
+func (ctx *blogContext) deleteComment(w http.ResponseWriter, r *http.Request) {
+ commentId := r.URL.Query().Get("id")
+
+ updateStmt, err := ctx.db.Prepare("DELETE FROM comments WHERE id = $1;")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not prepare update statement to DB: %v\n", err)
+ return
+ }
+ defer updateStmt.Close()
+
+ _, err = updateStmt.Exec(commentId)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not execute to DB: %v\n", err)
+ return
+ }
+
+ http.Redirect(w, r, r.Header.Get("Referer"), http.StatusFound)
+ return
+}
+
func (ctx *blogContext) viewPost(w http.ResponseWriter, r *http.Request) {
ctx.Rows = []models.Post{}
+ ctx.Comments = []models.Comment{}
+ ctx.IsAuth = false
postId := r.URL.Query().Get("id")
+ _, err := r.Cookie("paterissa_session_token")
+ if err == nil {
+ ctx.IsAuth = true
+ }
+
stmt, err := ctx.db.Prepare("SELECT p.id, u.name, p.time, p.brief, p.content FROM posts p INNER JOIN logins u ON p.user_id = u.id WHERE p.id = $1;")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
@@ -60,6 +158,39 @@ func (ctx *blogContext) viewPost(w http.ResponseWriter, r *http.Request) {
p.FormattedTime = p.Time.Format(time.ANSIC)
ctx.Post = p
+ commentStmt, err := ctx.db.Prepare("SELECT id, verified, time, name, content FROM comments WHERE post_id = $1;")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not prepare statement for DB: %v\n", err)
+ return
+ }
+ defer commentStmt.Close()
+
+ commentRows, err := commentStmt.Query(p.Id)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not prepare statement for DB: %v\n", err)
+ return
+ }
+ defer commentRows.Close()
+
+ for commentRows.Next() {
+ var c models.Comment
+
+ if err = commentRows.Scan(&c.Id, &c.IsVerified, &c.Time, &c.Name, &c.Content); err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal Error"))
+ ctx.err.Printf("Could not load row: %v\n", err)
+ return
+ }
+
+ c.FormattedTime = c.Time.Format(time.ANSIC)
+
+ ctx.Comments = append(ctx.Comments, c)
+ }
+
files := []string{
"ui/html/base.tmpl.html",
"ui/html/music_player.tmpl.html",
diff --git a/cmd/web/handlers/routes.go b/cmd/web/handlers/routes.go
index 1c14705..f6d8dc3 100644
--- a/cmd/web/handlers/routes.go
+++ b/cmd/web/handlers/routes.go
@@ -47,6 +47,9 @@ func RegisterEndpoints(app types.Application, db *sql.DB) *http.ServeMux {
blogRouter.HandleFunc("/", auth.CheckAndInvalidate(blog.index))
blogRouter.HandleFunc("/post/new", auth.Resolve(blog.post))
blogRouter.HandleFunc("/post", blog.viewPost)
+ blogRouter.HandleFunc("/comments/verify", auth.Resolve(blog.verifyComment))
+ blogRouter.HandleFunc("/comments/delete", auth.Resolve(blog.deleteComment))
+ blogRouter.HandleFunc("/comments/post", blog.comment)
blogRouter.HandleFunc("/login", login.handle)
blogRouter.HandleFunc("/logout", auth.Resolve(login.logout))
diff --git a/internal/dbmigrations.go b/internal/dbmigrations.go
index fcbaaaf..41180c7 100644
--- a/internal/dbmigrations.go
+++ b/internal/dbmigrations.go
@@ -56,4 +56,12 @@ func Migrate(db *sql.DB, webmaster string, passOne string, passTwo string) {
fmt.Fprintf(os.Stderr, "Unable to create cookies table: %v\n", err)
}
}
+
+ _, table_check = db.Query("SELECT * FROM comments;")
+ if table_check != nil {
+ _, err := db.Exec("CREATE TABLE comments (id SERIAL PRIMARY KEY, verified BOOLEAN NOT NULL, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, name TEXT NOT NULL, post_id INTEGER REFERENCES posts(id), content TEXT NOT NULL);")
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to create comments table: %v\n", err)
+ }
+ }
}
diff --git a/internal/models/comment.go b/internal/models/comment.go
new file mode 100644
index 0000000..df27ca3
--- /dev/null
+++ b/internal/models/comment.go
@@ -0,0 +1,12 @@
+package models
+
+import "time"
+
+type Comment struct {
+ Id int
+ IsVerified bool
+ Time time.Time
+ FormattedTime string
+ Name string
+ Content string
+}
diff --git a/static/app.css b/static/app.css
index 2ed671f..4e26607 100644
--- a/static/app.css
+++ b/static/app.css
@@ -22,6 +22,10 @@ pre {
word-wrap: inherit;
}
+.w100 {
+ width: 100%;
+}
+
.rss {
margin-left: 1.5em;
}
diff --git a/ui/html/pages/post.tmpl.html b/ui/html/pages/post.tmpl.html
index eefbd82..d3b78f0 100644
--- a/ui/html/pages/post.tmpl.html
+++ b/ui/html/pages/post.tmpl.html
@@ -5,4 +5,40 @@
<div class="card">
{{.Post.Content}}
</div>
+ <div class="card">
+ <h3>Leave Comment</h3>
+ <form action="/comments/post?id={{.Post.Id}}" method="POST">
+ <label for="comment"></label>
+ <textarea name="comment" id="comment" rows="5" class="w100" required></textarea><br><br>
+ <div class="flex-between">
+ <label for="name">Name:</label>
+ <input type="text" name="name" id="name" required>
+ <input type="submit" value="Post">
+ </div>
+ </form>
+ </div>
+ {{range .Comments}}
+ {{if .IsVerified}}
+ <div class="card">
+ <h3>{{.Name}} - {{.FormattedTime}}</h3>
+ {{if $.IsAuth}}
+ <div class="nav_tag right">
+ <a href="/comments/delete?id={{.Id}}" class="nav_tag right">Delete</a>
+ </div>
+ {{end}}
+ <p><pre>{{.Content}}</pre></p>
+ </div>
+ {{else}}
+ {{if $.IsAuth}}
+ <div class="card">
+ <h3>{{.Name}} - {{.FormattedTime}}</h3>
+ <div class="nav_tag right">
+ <a href="/comments/verify?id={{.Id}}" class="nav_tag right">Verify</a>
+ <a href="/comments/delete?id={{.Id}}" class="nav_tag right">Delete</a>
+ </div>
+ <p><pre>{{.Content}}</pre></p>
+ </div>
+ {{end}}
+ {{end}}
+ {{end}}
{{end}}