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"
|
2022-02-23 20:33:02 +00:00
|
|
|
"go.goblog.app/app/pkgs/bufferpool"
|
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
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) serveComment(w http.ResponseWriter, r *http.Request) {
|
2021-03-22 07:20:56 +00:00
|
|
|
id, err := strconv.Atoi(chi.URLParam(r, "id"))
|
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
}
|
2022-08-09 15:25:22 +00:00
|
|
|
row, err := a.db.QueryRow("select id, target, name, website, comment from comments where id = @id", sql.Named("id", id))
|
2021-03-22 07:20:56 +00:00
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
comment := &comment{}
|
|
|
|
if err = row.Scan(&comment.ID, &comment.Target, &comment.Name, &comment.Website, &comment.Comment); err == sql.ErrNoRows {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serve404(w, r)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
} else if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
2021-01-23 16:24:47 +00:00
|
|
|
}
|
2021-12-20 13:00:11 +00:00
|
|
|
_, bc := a.getBlog(r)
|
2022-01-30 15:40:53 +00:00
|
|
|
a.render(w, r, a.renderComment, &renderData{
|
2021-12-20 13:00:11 +00:00
|
|
|
Canonical: a.getFullAddress(bc.getRelativePath(path.Join(commentPath, strconv.Itoa(id)))),
|
|
|
|
Data: comment,
|
2021-03-22 07:20:56 +00:00
|
|
|
})
|
2021-01-23 16:24:47 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) createComment(w http.ResponseWriter, r *http.Request) {
|
2021-03-22 07:20:56 +00:00
|
|
|
// Check target
|
2021-06-06 12:39:42 +00:00
|
|
|
target := a.checkCommentTarget(w, r)
|
2021-03-22 07:20:56 +00:00
|
|
|
if target == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Check and clean comment
|
2021-09-01 14:38:08 +00:00
|
|
|
comment := cleanHTMLText(r.FormValue("comment"))
|
2021-03-22 07:20:56 +00:00
|
|
|
if comment == "" {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, "Comment is empty", http.StatusBadRequest)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
}
|
2021-09-01 14:38:08 +00:00
|
|
|
name := defaultIfEmpty(cleanHTMLText(r.FormValue("name")), "Anonymous")
|
|
|
|
website := cleanHTMLText(r.FormValue("website"))
|
2021-03-22 07:20:56 +00:00
|
|
|
// Insert
|
2022-08-09 15:25:22 +00:00
|
|
|
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))
|
2021-03-22 07:20:56 +00:00
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if commentID, err := result.LastInsertId(); err != nil {
|
|
|
|
// Serve error
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-03-22 07:20:56 +00:00
|
|
|
} else {
|
2021-12-20 13:00:11 +00:00
|
|
|
_, bc := a.getBlog(r)
|
|
|
|
commentAddress := bc.getRelativePath(path.Join(commentPath, strconv.Itoa(int(commentID))))
|
2021-03-22 07:20:56 +00:00
|
|
|
// Send webmention
|
2021-06-10 20:09:50 +00:00
|
|
|
_ = a.createWebmention(a.getFullAddress(commentAddress), a.getFullAddress(target))
|
2021-03-22 07:20:56 +00:00
|
|
|
// Redirect to comment
|
|
|
|
http.Redirect(w, r, commentAddress, http.StatusFound)
|
2021-01-23 16:24:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +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 == "" {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, "No target specified", http.StatusBadRequest)
|
2021-01-23 16:24:47 +00:00
|
|
|
return ""
|
2021-06-06 12:39:42 +00:00
|
|
|
} 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 {
|
2021-06-06 12:39:42 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-03-16 07:28:03 +00:00
|
|
|
func buildCommentsQuery(config *commentsRequestConfig) (query string, args []any) {
|
2022-02-23 20:33:02 +00:00
|
|
|
queryBuilder := bufferpool.Get()
|
|
|
|
defer bufferpool.Put(queryBuilder)
|
2021-07-23 15:26:14 +00:00
|
|
|
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 {
|
2021-07-23 15:26:14 +00:00
|
|
|
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
|
|
|
}
|
2021-07-23 15:26:14 +00:00
|
|
|
return queryBuilder.String(), args
|
2021-01-23 16:24:47 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +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)
|
2022-08-09 15:25:22 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
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 + ")"
|
2022-08-09 15:25:22 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (db *database) deleteComment(id int) error {
|
2022-08-09 15:25:22 +00:00
|
|
|
_, err := db.Exec("delete from comments where id = @id", sql.Named("id", id))
|
2021-01-23 16:24:47 +00:00
|
|
|
return err
|
|
|
|
}
|
2022-08-14 09:05:23 +00:00
|
|
|
|
|
|
|
func (a *goBlog) commentsEnabledForBlog(blog *configBlog) bool {
|
|
|
|
return blog.Comments != nil && blog.Comments.Enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
const commentsPostParam = "comments"
|
|
|
|
|
|
|
|
func (a *goBlog) commentsEnabledForPost(post *post) bool {
|
|
|
|
return post != nil && a.commentsEnabledForBlog(a.getBlogFromPost(post)) && post.firstParameter(commentsPostParam) != "false"
|
|
|
|
}
|