GoBlog/comments.go

147 lines
4.2 KiB
Go
Raw Normal View History

2021-01-23 16:24:47 +00:00
package main
import (
"database/sql"
"net/http"
2021-02-14 13:15:01 +00:00
"net/url"
2021-09-01 14:38:08 +00:00
"path"
2021-01-23 16:24:47 +00:00
"strconv"
2021-02-14 13:15:01 +00:00
"strings"
2021-01-23 16:24:47 +00:00
2021-03-03 17:19:55 +00:00
"github.com/go-chi/chi/v5"
2021-01-23 16:24:47 +00:00
)
2021-09-01 14:38:08 +00:00
const commentPath = "/comment"
2021-01-23 16:24:47 +00:00
type comment struct {
ID int
Target string
Name string
Website string
Comment string
}
func (a *goBlog) serveComment(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(chi.URLParam(r, "id"))
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
row, err := a.db.queryRow("select id, target, name, website, comment from comments where id = @id", sql.Named("id", id))
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
comment := &comment{}
if err = row.Scan(&comment.ID, &comment.Target, &comment.Name, &comment.Website, &comment.Comment); err == sql.ErrNoRows {
a.serve404(w, r)
return
} else if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
return
2021-01-23 16:24:47 +00:00
}
_, bc := a.getBlog(r)
a.render(w, r, templateComment, &renderData{
Canonical: a.getFullAddress(bc.getRelativePath(path.Join(commentPath, strconv.Itoa(id)))),
Data: comment,
})
2021-01-23 16:24:47 +00:00
}
func (a *goBlog) createComment(w http.ResponseWriter, r *http.Request) {
// Check target
target := a.checkCommentTarget(w, r)
if target == "" {
return
}
// Check and clean comment
2021-09-01 14:38:08 +00:00
comment := cleanHTMLText(r.FormValue("comment"))
if comment == "" {
a.serveError(w, r, "Comment is empty", http.StatusBadRequest)
return
}
2021-09-01 14:38:08 +00:00
name := defaultIfEmpty(cleanHTMLText(r.FormValue("name")), "Anonymous")
website := cleanHTMLText(r.FormValue("website"))
// Insert
result, err := a.db.exec("insert into comments (target, comment, name, website) values (@target, @comment, @name, @website)", sql.Named("target", target), sql.Named("comment", comment), sql.Named("name", name), sql.Named("website", website))
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
if commentID, err := result.LastInsertId(); err != nil {
// Serve error
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
} else {
_, bc := a.getBlog(r)
commentAddress := bc.getRelativePath(path.Join(commentPath, strconv.Itoa(int(commentID))))
// Send webmention
_ = a.createWebmention(a.getFullAddress(commentAddress), a.getFullAddress(target))
// Redirect to comment
http.Redirect(w, r, commentAddress, http.StatusFound)
2021-01-23 16:24:47 +00:00
}
}
func (a *goBlog) checkCommentTarget(w http.ResponseWriter, r *http.Request) string {
2021-01-23 16:24:47 +00:00
target := r.FormValue("target")
if target == "" {
a.serveError(w, r, "No target specified", http.StatusBadRequest)
2021-01-23 16:24:47 +00:00
return ""
} else if !strings.HasPrefix(target, a.cfg.Server.PublicAddress) {
a.serveError(w, r, "Bad target", http.StatusBadRequest)
2021-01-23 16:24:47 +00:00
return ""
}
2021-02-14 13:15:01 +00:00
targetURL, err := url.Parse(target)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
2021-01-23 16:24:47 +00:00
return ""
}
2021-02-14 13:15:01 +00:00
return targetURL.Path
2021-01-23 16:24:47 +00:00
}
2021-02-17 15:07:42 +00:00
type commentsRequestConfig struct {
offset, limit int
}
func buildCommentsQuery(config *commentsRequestConfig) (query string, args []interface{}) {
var queryBuilder strings.Builder
queryBuilder.WriteString("select id, target, name, website, comment from comments order by id desc")
2021-02-17 15:07:42 +00:00
if config.limit != 0 || config.offset != 0 {
queryBuilder.WriteString(" limit @limit offset @offset")
2021-02-17 15:07:42 +00:00
args = append(args, sql.Named("limit", config.limit), sql.Named("offset", config.offset))
2021-01-23 16:24:47 +00:00
}
return queryBuilder.String(), args
2021-01-23 16:24:47 +00:00
}
func (db *database) getComments(config *commentsRequestConfig) ([]*comment, error) {
2021-01-23 16:24:47 +00:00
comments := []*comment{}
2021-02-17 15:07:42 +00:00
query, args := buildCommentsQuery(config)
rows, err := db.query(query, args...)
2021-01-23 16:24:47 +00:00
if err != nil {
return nil, err
}
for rows.Next() {
c := &comment{}
err = rows.Scan(&c.ID, &c.Target, &c.Name, &c.Website, &c.Comment)
if err != nil {
return nil, err
}
comments = append(comments, c)
}
return comments, nil
}
func (db *database) countComments(config *commentsRequestConfig) (count int, err error) {
2021-02-17 15:07:42 +00:00
query, params := buildCommentsQuery(config)
query = "select count(*) from (" + query + ")"
row, err := db.queryRow(query, params...)
2021-01-23 16:24:47 +00:00
if err != nil {
return
}
2021-02-17 15:07:42 +00:00
err = row.Scan(&count)
return
2021-01-23 16:24:47 +00:00
}
func (db *database) deleteComment(id int) error {
_, err := db.exec("delete from comments where id = @id", sql.Named("id", id))
2021-01-23 16:24:47 +00:00
return err
}